| 0/0 |
2026/05/16 14:09 |
flow |
patching-compressed |
53m
Results: map[Fixes:map[Hash:c04148f915e5ba7947752e6348e0da4cdab1329e Title:Input: add driver for USB VoIP phones with CM109 chipset] KernelBranch:master KernelCommit:5d6919055dec134de3c40167a490f33c74c12581 KernelRepo:git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git PatchDescription:Input: cm109 - fix double URB submission race conditions
An analysis of a kernel crash reveals a race condition in the cm109
input driver that leads to a double-add bug in the USB core's URB
enqueueing mechanism. The crash `list_add double add` occurs in
`usb_hcd_link_urb_to_ep()` when an URB is submitted while it is already
on the endpoint's queue.
In `drivers/input/misc/cm109.c`, the control URB (`dev->urb_ctl`) is
submitted in several places: `cm109_input_open()`,
`cm109_toggle_buzzer_async()`, and `cm109_submit_ctl()`.
There are two distinct race conditions that can lead to a double submit.
First, a race between `cm109_input_open()` and
`cm109_toggle_buzzer_async()`: because `cm109_input_ev()` does not check
if the device is actually open (`dev->open`), and `cm109_input_open()`
does not hold the `dev->ctl_submit_lock` when submitting the URB, these
two functions can race and call `usb_submit_urb(dev->urb_ctl)`
concurrently. Second, a race between `cm109_submit_ctl()` and
`cm109_toggle_buzzer_async()`: if `cm109_toggle_buzzer_async()` is
called while `dev->urb_irq` is in flight, it sees `dev->ctl_urb_pending
== 0`, sets it to 1, and submits `dev->urb_ctl`. When `dev->urb_irq`
completes, it calls `cm109_submit_ctl()`. However, `cm109_submit_ctl()`
does not check `dev->ctl_urb_pending` before submitting `dev->urb_ctl`,
leading to a double submit if the URB is already in flight.
To fix these bugs, we properly serialize URB submissions and state
changes using `dev->ctl_submit_lock`. We serialize URB submission in
`cm109_input_open()` by acquiring `dev->ctl_submit_lock` before
submitting the URB, and changing the memory allocation flag for
`usb_submit_urb()` from `GFP_KERNEL` to `GFP_ATOMIC`. We check
`dev->open` in `cm109_toggle_buzzer_async()` to prevent the event
handler from submitting URBs and restarting the polling loop when the
device is closed. We safely update the `dev->open` flag under the
`dev->ctl_submit_lock` in both `cm109_input_open()` and
`cm109_input_close()` to ensure perfect synchronization with the event
handler. Finally, we check `dev->ctl_urb_pending` in
`cm109_submit_ctl()` so that if `dev->ctl_urb_pending` is already 1,
`cm109_submit_ctl()` returns immediately to prevent a double submit and
to avoid modifying the transfer buffer (`dev->ctl_data`) while the URB
is in flight. PatchDiff:diff --git a/drivers/input/misc/cm109.c b/drivers/input/misc/cm109.c
index 353d3c1d3..4479233d0 100644
--- a/drivers/input/misc/cm109.c
+++ b/drivers/input/misc/cm109.c
@@ -366,6 +366,9 @@ static void cm109_submit_ctl(struct cm109_dev *dev)
if (unlikely(dev->shutdown))
return;
+ if (dev->ctl_urb_pending)
+ return;
+
if (dev->buzzer_state)
dev->ctl_data->byte[HID_OR0] |= BUZZER_ON;
else
@@ -476,6 +479,9 @@ static void cm109_toggle_buzzer_async(struct cm109_dev *dev)
{
guard(spinlock_irqsave)(&dev->ctl_submit_lock);
+ if (!dev->open)
+ return;
+
if (dev->ctl_urb_pending) {
/* URB completion will resubmit */
dev->buzzer_pending = 1;
@@ -562,15 +568,19 @@ static int cm109_input_open(struct input_dev *idev)
dev->ctl_data->byte[HID_OR2] = dev->keybit;
dev->ctl_data->byte[HID_OR3] = 0x00;
+ spin_lock_irq(&dev->ctl_submit_lock);
+ dev->open = 1;
dev->ctl_urb_pending = 1;
- error = usb_submit_urb(dev->urb_ctl, GFP_KERNEL);
- if (!error) {
- dev->open = 1;
- return 0;
+ error = usb_submit_urb(dev->urb_ctl, GFP_ATOMIC);
+ if (error) {
+ dev->ctl_urb_pending = 0;
+ dev->open = 0;
}
- }
+ spin_unlock_irq(&dev->ctl_submit_lock);
- dev->ctl_urb_pending = 0;
+ if (!error)
+ return 0;
+ }
usb_autopm_put_interface(dev->intf);
dev_err(&dev->intf->dev, "%s: usb_submit_urb (urb_ctl) failed %d\n",
@@ -584,13 +594,16 @@ static void cm109_input_close(struct input_dev *idev)
struct cm109_dev *dev = input_get_drvdata(idev);
scoped_guard(mutex, &dev->pm_mutex) {
+ spin_lock_irq(&dev->ctl_submit_lock);
+ dev->open = 0;
+ spin_unlock_irq(&dev->ctl_submit_lock);
+
/*
* 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);
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/16 14:09 |
action |
base-commit-picker |
0m
Results: map[KernelBranch:master KernelCommit:5d6919055dec134de3c40167a490f33c74c12581 KernelRepo:git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git]
|
| 2/1 |
2026/05/16 14:09 |
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>
#ifndef __NR_bpf
#define __NR_bpf 321
#endif
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;
}
#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);
}
#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 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[2] = {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)) {}
// socket$inet_tcp arguments: [
// domain: const = 0x2 (8 bytes)
// type: const = 0x1 (8 bytes)
// proto: const = 0x0 (4 bytes)
// ]
// returns sock_tcp
res = syscall(__NR_socket, /*domain=*/2ul, /*type=*/1ul, /*proto=*/0);
if (res != -1)
r[0] = res;
// bpf$BPF_PROG_RAW_TRACEPOINT_LOAD arguments: [
// cmd: const = 0x5 (8 bytes)
// arg: nil
// size: len = 0x0 (8 bytes)
// ]
// returns fd_bpf_prog_raw_tracepoint
syscall(__NR_bpf, /*cmd=*/5ul, /*arg=*/0ul, /*size=*/0ul);
// ioctl$sock_SIOCGIFINDEX arguments: [
// fd: sock (resource)
// cmd: const = 0x8933 (4 bytes)
// arg: ptr[out, ifreq_dev_t[devnames, ifindex]] {
// ifreq_dev_t[devnames, ifindex] {
// ifr_ifrn: buffer: {73 79 7a 5f 74 75 6e 00 00 00 00 00 00 00 00 00} (length 0x10)
// elem: ifindex (resource)
// pad = 0x0 (20 bytes)
// }
// }
// ]
memcpy((void*)0x200000000180, "syz_tun\000\000\000\000\000\000\000\000\000", 16);
syscall(__NR_ioctl, /*fd=*/r[0], /*cmd=*/0x8933, /*arg=*/0x200000000180ul);
// bpf$BPF_LINK_CREATE_XDP arguments: [
// cmd: const = 0x1c (8 bytes)
// arg: nil
// size: len = 0x0 (8 bytes)
// ]
// returns fd_bpf_link
syscall(__NR_bpf, /*cmd=*/0x1cul, /*arg=*/0ul, /*size=*/0ul);
// 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} (length 0x2)
// }
// }
// }
// conn_descs: nil
// ]
// returns fd_usb
memcpy((void*)0x200000000000, "... [truncated large byte array] ...", 38);
memcpy((void*)0x200000000026, "\x8f\xcf", 2);
syz_usb_connect(/*speed=*/0, /*dev_len=*/0x2d, /*dev=*/0x200000000000, /*conn_descs=*/0);
// 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;
// ioctl$KIOCSOUND arguments: [
// fd: fd_tty (resource)
// cmd: const = 0x4b2f (4 bytes)
// arg: intptr = 0x0 (8 bytes)
// ]
syscall(__NR_ioctl, /*fd=*/r[1], /*cmd=*/0x4b2f, /*arg=*/0ul);
return 0;
}
]
|
| 3/1 |
2026/05/16 14:09 |
action |
kernel-checkouter |
0m
Results: map[KernelSrc:/app/workdir/cache/src/ecd8bb68855fdd08aa08215ac485989bac875215]
|
| 4/1 |
2026/05/16 14:09 |
action |
kernel-builder |
0m
Results: map[KernelObj:/app/workdir/cache/build/68d3107e8d008623f60038a4883237f0849af1c4]
|
| 5/1 |
2026/05/16 14:09 |
action |
crash-reproducer |
0m
Results: map[OtherCrashReports:<nil> ReproducedBugTitle:BUG: corrupted list in usb_hcd_link_urb_to_ep ReproducedCrashReport:list_add double add: new=ffff8880243bcb18, prev=ffff8880243bcb18, next=ffff888020f2e078.
------------[ cut here ]------------
kernel BUG at lib/list_debug.c:37!
Oops: invalid opcode: 0000 [#1] SMP KASAN PTI
CPU: 1 UID: 0 PID: 8662 Comm: syz.1.1154 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:__list_add_valid_or_report+0xa5/0x130 lib/list_debug.c:35
Code: 74 12 b0 01 5b 41 5c 41 5d 41 5e 41 5f 5d c3 cc cc cc cc cc 48 c7 c7 a0 e0 e7 8b 4c 89 fe 4c 89 f2 48 89 d9 e8 cc c2 6f fc 90 <0f> 0b 48 c7 c7 80 de e7 8b e8 bd c2 6f fc 90 0f 0b 48 c7 c7 40 df
RSP: 0018:ffffc90007cf7950 EFLAGS: 00010046
RAX: 0000000000000058 RBX: ffff888020f2e078 RCX: 07ffa03edd56da00
RDX: 0000000000000000 RSI: 0000000080000004 RDI: 0000000000000000
RBP: 1ffff110041e5c10 R08: ffffc90007cf76c7 R09: 1ffff92000f9eed8
R10: dffffc0000000000 R11: fffff52000f9eed9 R12: 1ffff11004877963
R13: dffffc0000000000 R14: ffff8880243bcb18 R15: ffff8880243bcb18
FS: 00007f62f8f336c0(0000) GS:ffff8880ebf64000(0000) knlGS:0000000000000000
CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 0000200000000180 CR3: 00000000280d4000 CR4: 00000000000006f0
Call Trace:
<TASK>
__list_add_valid include/linux/list.h:96 [inline]
__list_add include/linux/list.h:158 [inline]
list_add_tail include/linux/list.h:191 [inline]
usb_hcd_link_urb_to_ep+0x1d2/0x330 drivers/usb/core/hcd.c:1154
dummy_urb_enqueue+0x2a1/0x780 drivers/usb/gadget/udc/dummy_hcd.c:1292
usb_hcd_submit_urb+0x328/0x1b70 drivers/usb/core/hcd.c:1542
cm109_submit_buzz_toggle drivers/input/misc/cm109.c:351 [inline]
cm109_toggle_buzzer_async+0x114/0x250 drivers/input/misc/cm109.c:484
cm109_input_ev+0xed/0x1b0 drivers/input/misc/cm109.c:615
input_event_dispose+0x80/0x6b0 drivers/input/input.c:322
input_inject_event+0x1dc/0x330 drivers/input/input.c:424
kd_sound_helper+0x101/0x210 drivers/tty/vt/keyboard.c:257
input_handler_for_each_handle+0x101/0x1c0 drivers/input/input.c:2537
kd_mksound+0x96/0x130 drivers/tty/vt/keyboard.c:281
vt_k_ioctl drivers/tty/vt/vt_ioctl.c:303 [inline]
vt_ioctl+0x1c2d/0x20c0 drivers/tty/vt/vt_ioctl.c:743
tty_ioctl+0x928/0xde0 drivers/tty/tty_io.c:2792
vfs_ioctl fs/ioctl.c:51 [inline]
__do_sys_ioctl fs/ioctl.c:597 [inline]
__se_sys_ioctl+0xfc/0x170 fs/ioctl.c:583
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0x15f/0xf80 arch/x86/entry/syscall_64.c:94
entry_SYSCALL_64_after_hwframe+0x77/0x7f
RIP: 0033:0x7f62f7f9ce59
Code: ff c3 66 2e 0f 1f 84 00 00 00 00 00 0f 1f 44 00 00 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 c7 c1 e8 ff ff ff f7 d8 64 89 01 48
RSP: 002b:00007f62f8f33028 EFLAGS: 00000246 ORIG_RAX: 0000000000000010
RAX: ffffffffffffffda RBX: 00007f62f8215fa0 RCX: 00007f62f7f9ce59
RDX: 0000000000000000 RSI: 0000000000004b2f RDI: 0000000000000005
RBP: 00007f62f8032d6f R08: 0000000000000000 R09: 0000000000000000
R10: 0000000000000000 R11: 0000000000000246 R12: 0000000000000000
R13: 00007f62f8216038 R14: 00007f62f8215fa0 R15: 00007ffc9ebfae98
</TASK>
Modules linked in:
---[ end trace 0000000000000000 ]---
RIP: 0010:__list_add_valid_or_report+0xa5/0x130 lib/list_debug.c:35
Code: 74 12 b0 01 5b 41 5c 41 5d 41 5e 41 5f 5d c3 cc cc cc cc cc 48 c7 c7 a0 e0 e7 8b 4c 89 fe 4c 89 f2 48 89 d9 e8 cc c2 6f fc 90 <0f> 0b 48 c7 c7 80 de e7 8b e8 bd c2 6f fc 90 0f 0b 48 c7 c7 40 df
RSP: 0018:ffffc90007cf7950 EFLAGS: 00010046
RAX: 0000000000000058 RBX: ffff888020f2e078 RCX: 07ffa03edd56da00
RDX: 0000000000000000 RSI: 0000000080000004 RDI: 0000000000000000
RBP: 1ffff110041e5c10 R08: ffffc90007cf76c7 R09: 1ffff92000f9eed8
R10: dffffc0000000000 R11: fffff52000f9eed9 R12: 1ffff11004877963
R13: dffffc0000000000 R14: ffff8880243bcb18 R15: ffff8880243bcb18
FS: 00007f62f8f336c0(0000) GS:ffff8880ebf64000(0000) knlGS:0000000000000000
CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 0000200000000180 CR3: 00000000280d4000 CR4: 00000000000006f0
ReproducedFaultInjection:]
|
| 6/1 |
2026/05/16 14:09 |
action |
codesearch-prepare |
0m
Results: map[Index:codesearch-index]
|
| 7/1 |
2026/05/16 14:10 |
agent |
debugger |
0m
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:
list_add double add: new=ffff8880243bcb18, prev=ffff8880243bcb18, next=ffff888020f2e078.
------------[ cut here ]------------
kernel BUG at lib/list_debug.c:37!
Oops: invalid opcode: 0000 [#1] SMP KASAN PTI
CPU: 1 UID: 0 PID: 8662 Comm: syz.1.1154 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:__list_add_valid_or_report+0xa5/0x130 lib/list_debug.c:35
Code: 74 12 b0 01 5b 41 5c 41 5d 41 5e 41 5f 5d c3 cc cc cc cc cc 48 c7 c7 a0 e0 e7 8b 4c 89 fe 4c 89 f2 48 89 d9 e8 cc c2 6f fc 90 <0f> 0b 48 c7 c7 80 de e7 8b e8 bd c2 6f fc 90 0f 0b 48 c7 c7 40 df
RSP: 0018:ffffc90007cf7950 EFLAGS: 00010046
RAX: 0000000000000058 RBX: ffff888020f2e078 RCX: 07ffa03edd56da00
RDX: 0000000000000000 RSI: 0000000080000004 RDI: 0000000000000000
RBP: 1ffff110041e5c10 R08: ffffc90007cf76c7 R09: 1ffff92000f9eed8
R10: dffffc0000000000 R11: fffff52000f9eed9 R12: 1ffff11004877963
R13: dffffc0000000000 R14: ffff8880243bcb18 R15: ffff8880243bcb18
FS: 00007f62f8f336c0(0000) GS:ffff8880ebf64000(0000) knlGS:0000000000000000
CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 0000200000000180 CR3: 00000000280d4000 CR4: 00000000000006f0
Call Trace:
<TASK>
__list_add_valid include/linux/list.h:96 [inline]
__list_add include/linux/list.h:158 [inline]
list_add_tail include/linux/list.h:191 [inline]
usb_hcd_link_urb_to_ep+0x1d2/0x330 drivers/usb/core/hcd.c:1154
dummy_urb_enqueue+0x2a1/0x780 drivers/usb/gadget/udc/dummy_hcd.c:1292
usb_hcd_submit_urb+0x328/0x1b70 drivers/usb/core/hcd.c:1542
cm109_submit_buzz_toggle drivers/input/misc/cm109.c:351 [inline]
cm109_toggle_buzzer_async+0x114/0x250 drivers/input/misc/cm109.c:484
cm109_input_ev+0xed/0x1b0 drivers/input/misc/cm109.c:615
input_event_dispose+0x80/0x6b0 drivers/input/input.c:322
input_inject_event+0x1dc/0x330 drivers/input/input.c:424
kd_sound_helper+0x101/0x210 drivers/tty/vt/keyboard.c:257
input_handler_for_each_handle+0x101/0x1c0 drivers/input/input.c:2537
kd_mksound+0x96/0x130 drivers/tty/vt/keyboard.c:281
vt_k_ioctl drivers/tty/vt/vt_ioctl.c:303 [inline]
vt_ioctl+0x1c2d/0x20c0 drivers/tty/vt/vt_ioctl.c:743
tty_ioctl+0x928/0xde0 drivers/tty/tty_io.c:2792
vfs_ioctl fs/ioctl.c:51 [inline]
__do_sys_ioctl fs/ioctl.c:597 [inline]
__se_sys_ioctl+0xfc/0x170 fs/ioctl.c:583
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0x15f/0xf80 arch/x86/entry/syscall_64.c:94
entry_SYSCALL_64_after_hwframe+0x77/0x7f
RIP: 0033:0x7f62f7f9ce59
Code: ff c3 66 2e 0f 1f 84 00 00 00 00 00 0f 1f 44 00 00 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 c7 c1 e8 ff ff ff f7 d8 64 89 01 48
RSP: 002b:00007f62f8f33028 EFLAGS: 00000246 ORIG_RAX: 0000000000000010
RAX: ffffffffffffffda RBX: 00007f62f8215fa0 RCX: 00007f62f7f9ce59
RDX: 0000000000000000 RSI: 0000000000004b2f RDI: 0000000000000005
RBP: 00007f62f8032d6f R08: 0000000000000000 R09: 0000000000000000
R10: 0000000000000000 R11: 0000000000000246 R12: 0000000000000000
R13: 00007f62f8216038 R14: 00007f62f8215fa0 R15: 00007ffc9ebfae98
</TASK>
Modules linked in:
---[ end trace 0000000000000000 ]---
RIP: 0010:__list_add_valid_or_report+0xa5/0x130 lib/list_debug.c:35
Code: 74 12 b0 01 5b 41 5c 41 5d 41 5e 41 5f 5d c3 cc cc cc cc cc 48 c7 c7 a0 e0 e7 8b 4c 89 fe 4c 89 f2 48 89 d9 e8 cc c2 6f fc 90 <0f> 0b 48 c7 c7 80 de e7 8b e8 bd c2 6f fc 90 0f 0b 48 c7 c7 40 df
RSP: 0018:ffffc90007cf7950 EFLAGS: 00010046
RAX: 0000000000000058 RBX: ffff888020f2e078 RCX: 07ffa03edd56da00
RDX: 0000000000000000 RSI: 0000000080000004 RDI: 0000000000000000
RBP: 1ffff110041e5c10 R08: ffffc90007cf76c7 R09: 1ffff92000f9eed8
R10: dffffc0000000000 R11: fffff52000f9eed9 R12: 1ffff11004877963
R13: dffffc0000000000 R14: ffff8880243bcb18 R15: ffff8880243bcb18
FS: 00007f62f8f336c0(0000) GS:ffff8880ebf64000(0000) knlGS:0000000000000000
CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 0000200000000180 CR3: 00000000280d4000 CR4: 00000000000006f0
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>
#ifndef __NR_bpf
#define __NR_bpf 321
#endif
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;
}
#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);
}
#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 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[2] = {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)) {}
// socket$inet_tcp arguments: [
// domain: const = 0x2 (8 bytes)
// type: const = 0x1 (8 bytes)
// proto: const = 0x0 (4 bytes)
// ]
// returns sock_tcp
res = syscall(__NR_socket, /*domain=*/2ul, /*type=*/1ul, /*proto=*/0);
if (res != -1)
r[0] = res;
// bpf$BPF_PROG_RAW_TRACEPOINT_LOAD arguments: [
// cmd: const = 0x5 (8 bytes)
// arg: nil
// size: len = 0x0 (8 bytes)
// ]
// returns fd_bpf_prog_raw_tracepoint
syscall(__NR_bpf, /*cmd=*/5ul, /*arg=*/0ul, /*size=*/0ul);
// ioctl$sock_SIOCGIFINDEX arguments: [
// fd: sock (resource)
// cmd: const = 0x8933 (4 bytes)
// arg: ptr[out, ifreq_dev_t[devnames, ifindex]] {
// ifreq_dev_t[devnames, ifindex] {
// ifr_ifrn: buffer: {73 79 7a 5f 74 75 6e 00 00 00 00 00 00 00 00 00} (length 0x10)
// elem: ifindex (resource)
// pad = 0x0 (20 bytes)
// }
// }
// ]
memcpy((void*)0x200000000180, "syz_tun\000\000\000\000\000\000\000\000\000", 16);
syscall(__NR_ioctl, /*fd=*/r[0], /*cmd=*/0x8933, /*arg=*/0x200000000180ul);
// bpf$BPF_LINK_CREATE_XDP arguments: [
// cmd: const = 0x1c (8 bytes)
// arg: nil
// size: len = 0x0 (8 bytes)
// ]
// returns fd_bpf_link
syscall(__NR_bpf, /*cmd=*/0x1cul, /*arg=*/0ul, /*size=*/0ul);
// 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} (length 0x2)
// }
// }
// }
// conn_descs: nil
// ]
// returns fd_usb
memcpy((void*)0x200000000000, "... [truncated large byte array] ...", 38);
memcpy((void*)0x200000000026, "\x8f\xcf", 2);
syz_usb_connect(/*speed=*/0, /*dev_len=*/0x2d, /*dev=*/0x200000000000, /*conn_descs=*/0);
// 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;
// ioctl$KIOCSOUND arguments: [
// fd: fd_tty (resource)
// cmd: const = 0x4b2f (4 bytes)
// arg: intptr = 0x0 (8 bytes)
// ]
syscall(__NR_ioctl, /*fd=*/r[1], /*cmd=*/0x4b2f, /*arg=*/0ul);
return 0;
}
Reply: An analysis of the kernel crash reveals a race condition in the `cm109` input driver that leads to a double-add bug in the USB core's URB enqueueing mechanism.
### Root Cause Analysis
The crash `list_add double add` occurs in `usb_hcd_link_urb_to_ep()` when an URB is submitted while it is already on the endpoint's queue.
In `drivers/input/misc/cm109.c`, the control URB (`dev->urb_ctl`) is submitted in two main places:
1. **`cm109_input_open()`**: Called when the input device is opened. It submits the URB to initialize the device. Crucially, it does this **without** holding the `dev->ctl_submit_lock`.
2. **`cm109_input_ev()`**: The event handler for the input device. It can be triggered at any time by the input core (e.g., via `input_inject_event()` from `kd_mksound()` when a console beep is requested). When it receives a `SND_TONE` or `SND_BELL` event, it calls `cm109_toggle_buzzer_async()`, which acquires `dev->ctl_submit_lock` and submits the same `dev->urb_ctl`.
Because `cm109_input_ev()` does not check if the device is actually open (`dev->open`), and `cm109_input_open()` does not hold the `dev->ctl_submit_lock` when submitting the URB, these two functions can race and call `usb_submit_urb(dev->urb_ctl)` concurrently.
### Race Condition Details
While `usb_submit_urb()` has a safeguard check (`if (urb->hcpriv) return -EBUSY;`) to prevent submitting an already active URB, this check is **not protected by any lock**.
If `cm109_input_open()` and `cm109_input_ev()` call `usb_submit_urb()` at the exact same time:
1. Both threads read `urb->hcpriv` as `NULL`.
2. Both threads proceed to call `usb_hcd_submit_urb()`.
3. Both threads reach the HCD's `urb_enqueue` callback (e.g., `dummy_urb_enqueue()`).
4. The first thread successfully adds `urb->urb_list` to the endpoint's list (`urb->ep->urb_list`).
5. The second thread attempts to add the same `urb->urb_list` to the list, triggering the `list_add double add` BUG in `__list_add_valid()`.
### Proposed Fix
To fix this bug, the URB submission in `cm109_input_open()` must be serialized with `cm109_toggle_buzzer_async()`, and the event handler must not submit URBs if the device is closed.
1. **Serialize URB submission in `cm109_input_open()`**:
Acquire `dev->ctl_submit_lock` before submitting the URB. Because we are holding a spinlock, the memory allocation flag for `usb_submit_urb()` must be changed from `GFP_KERNEL` to `GFP_ATOMIC`.
2. **Check `dev->open` in `cm109_toggle_buzzer_async()`**:
Prevent the event handler from submitting URBs and restarting the polling loop when the device is closed.
3. **Safely update `dev->open`**:
Update the `dev->open` flag under the `dev->ctl_submit_lock` in both `cm109_input_open()` and `cm109_input_close()` to ensure perfect synchronization with the event handler.
Here is how the fix should look:
**In `cm109_input_open()`:**
```c
/* 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;
spin_lock_irq(&dev->ctl_submit_lock);
dev->open = 1;
dev->ctl_urb_pending = 1;
error = usb_submit_urb(dev->urb_ctl, GFP_ATOMIC); // Changed to GFP_ATOMIC
if (error) {
dev->ctl_urb_pending = 0;
dev->open = 0;
}
spin_unlock_irq(&dev->ctl_submit_lock);
if (!error)
return 0;
```
**In `cm109_input_close()`:**
```c
scoped_guard(mutex, &dev->pm_mutex) {
spin_lock_irq(&dev->ctl_submit_lock);
dev->open = 0;
spin_unlock_irq(&dev->ctl_submit_lock);
/* kill any pending urbs */
cm109_stop_traffic(dev);
}
```
**In `cm109_toggle_buzzer_async()`:**
```c
static void cm109_toggle_buzzer_async(struct cm109_dev *dev)
{
guard(spinlock_irqsave)(&dev->ctl_submit_lock);
if (!dev->open)
return;
if (dev->ctl_urb_pending) {
/* URB completion will resubmit */
dev->buzzer_pending = 1;
} else {
dev->ctl_urb_pending = 1;
cm109_submit_buzz_toggle(dev);
}
}
```
|
| 8/2 |
2026/05/16 14:10 |
llm |
debugger |
0m
Model:
Tokens:
input: 13588
output: 253
thoughts: 978
Thoughts: **Analyzing a `list_add` Double Add Crash**
Okay, so we've hit a pretty nasty crash: a `list_add double add` error. The message gives us a starting point: `new=ffff8880243bcb18, prev=ffff8880243bcb18, next=ffff888020f2e078.` This immediately tells me that the node we're trying to add, `new`, already *thinks* it's on a list, or at least its `prev` pointer is pointing to itself. I need to clarify though, this message tells me that `prev` is actually equal to `new`, which, according to the `__list_add` function's arguments, means `new->prev == new`, i.e., `new` is its own predecessor.
Let's quickly refresh my memory on how this list addition should work. Right, `__list_add_valid` is called for a few checks. And here are the checks:
```c
if (CHECK_DATA_CORRUPTION(next->prev != prev,
"list_add corruption. next->prev should be prev (%px), but was %px. (next=%px).\n",
prev, next->prev, next) ||
CHECK_DATA_CORRUPTION(prev->next != next,
"list_add corruption. prev->next should be next (%px), but was %px. (prev=%px).\n",
next, prev->next, prev) ||
CHECK_DATA_CORRUPTION(new == prev || new == next,
"list_add double add: new=%px, prev=%px, next=%px.\n",
new, prev, next))
return false;
```
Aha! The third check: `new == prev || new == next`. It means that `new` is *already* one of the list's boundary nodes! So our crash is happening because we are attempting to add a node to a list where either `list_add_tail(new, head)` was called and `new == head->prev` (since the `prev` argument passed into `__list_add` is the same as `head->prev` in the case of `list_add_tail`) or we are calling `list_add(new, head)` and `new == head`. That explains it.
Now, where did this happen? The stack trace is our next lead:
```
__list_add_valid include/linux/list.h:96 [inline]
__list_add include/linux/list.h:158 [inline]
list_add_tail include/linux/list.h:191 [inline]
usb_hcd_link_urb_to_ep+0x1d2/0x330 drivers/usb/core/hcd.c:1154
dummy_urb_enqueue+0x2a1/0x780 drivers/usb/gadget/udc/dummy_hcd.c:1292
usb_hcd_submit_urb+0x328/0x1b70 drivers/usb/core/hcd.c:1542
cm109_submit_buzz_toggle drivers/input/misc/cm109.c:351 [inline]
cm109_toggle_buzzer_async+0x114/0x250 drivers/input/misc/cm109.c:484
cm109_input_ev+0xed/0x1b0 drivers/input/misc/cm109.c:615
input_event_dispose+0x80/0x6b0 drivers/input/input.c:322
input_inject_event+0x1dc/0x330 drivers/input/input.c:424
kd_sound_helper+0x101/0x210 drivers/tty/vt/keyboard.c:257
input_handler_for_each_handle+0x101/0x1c0 drivers/input/input.c:2537
kd_mksound+0x96/0x130 drivers/tty/vt/keyboard.c:281
vt_k_ioctl drivers/tty/vt/vt_ioctl.c:303 [inline]
vt_ioctl+0x1c2d/0x20c0 drivers
|
| 9/2 |
2026/05/16 14:10 |
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]
|
| 10/2 |
2026/05/16 14:10 |
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]
|
| 11/2 |
2026/05/16 14:10 |
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]
|
| 12/2 |
2026/05/16 14:10 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/core/hcd.c IncludeLines:true Name:usb_hcd_link_urb_to_ep]
Results: map[SourceCode:1111: /**
1112: * usb_hcd_link_urb_to_ep - add an URB to its endpoint queue
1113: * @hcd: host controller to which @urb was submitted
1114: * @urb: URB being submitted
1115: *
1116: * Host controller drivers should call this routine in their enqueue()
1117: * method. The HCD's private spinlock must be held and interrupts must
1118: * be disabled. The actions carried out here are required for URB
1119: * submission, as well as for endpoint shutdown and for usb_kill_urb.
1120: *
1121: * Return: 0 for no error, otherwise a negative error code (in which case
1122: * the enqueue() method must fail). If no error occurs but enqueue() fails
1123: * anyway, it must call usb_hcd_unlink_urb_from_ep() before releasing
1124: * the private spinlock and returning.
1125: */
1126: int usb_hcd_link_urb_to_ep(struct usb_hcd *hcd, struct urb *urb)
1127: {
1128: int rc = 0;
1129:
1130: spin_lock(&hcd_urb_list_lock);
1131:
1132: /* Check that the URB isn't being killed */
1133: if (unlikely(atomic_read(&urb->reject))) {
1134: rc = -EPERM;
1135: goto done;
1136: }
1137:
1138: if (unlikely(!urb->ep->enabled)) {
1139: rc = -ENOENT;
1140: goto done;
1141: }
1142:
1143: if (unlikely(!urb->dev->can_submit)) {
1144: rc = -EHOSTUNREACH;
1145: goto done;
1146: }
1147:
1148: /*
1149: * Check the host controller's state and add the URB to the
1150: * endpoint's queue.
1151: */
1152: if (HCD_RH_RUNNING(hcd)) {
1153: urb->unlinked = 0;
1154: list_add_tail(&urb->urb_list, &urb->ep->urb_list);
1155: } else {
1156: rc = -ESHUTDOWN;
1157: goto done;
1158: }
1159: done:
1160: spin_unlock(&hcd_urb_list_lock);
1161: return rc;
1162: }
SourceFile:drivers/usb/core/hcd.c]
|
| 13/2 |
2026/05/16 14:10 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/gadget/udc/dummy_hcd.c IncludeLines:true Name:dummy_urb_enqueue]
Results: map[SourceCode:1267: static int dummy_urb_enqueue(
1268: struct usb_hcd *hcd,
1269: struct urb *urb,
1270: gfp_t mem_flags
1271: ) {
1272: struct dummy_hcd *dum_hcd;
1273: struct urbp *urbp;
1274: unsigned long flags;
1275: int rc;
1276:
1277: urbp = kmalloc_obj(*urbp, mem_flags);
1278: if (!urbp)
1279: return -ENOMEM;
1280: urbp->urb = urb;
1281: urbp->miter_started = 0;
1282:
1283: dum_hcd = hcd_to_dummy_hcd(hcd);
1284: spin_lock_irqsave(&dum_hcd->dum->lock, flags);
1285:
1286: rc = dummy_validate_stream(dum_hcd, urb);
1287: if (rc) {
1288: kfree(urbp);
1289: goto done;
1290: }
1291:
1292: rc = usb_hcd_link_urb_to_ep(hcd, urb);
1293: if (rc) {
1294: kfree(urbp);
1295: goto done;
1296: }
1297:
1298: if (!dum_hcd->udev) {
1299: dum_hcd->udev = urb->dev;
1300: usb_get_dev(dum_hcd->udev);
1301: } else if (unlikely(dum_hcd->udev != urb->dev))
1302: dev_err(dummy_dev(dum_hcd), "usb_device address has changed!\n");
1303:
1304: list_add_tail(&urbp->urbp_list, &dum_hcd->urbp_list);
1305: urb->hcpriv = urbp;
1306: if (!dum_hcd->next_frame_urbp)
1307: dum_hcd->next_frame_urbp = urbp;
1308: if (usb_pipetype(urb->pipe) == PIPE_CONTROL)
1309: urb->error_count = 1; /* mark as a new urb */
1310:
1311: /* kick the scheduler, it'll do the rest */
1312: if (!dum_hcd->timer_pending) {
1313: dum_hcd->timer_pending = 1;
1314: hrtimer_start(&dum_hcd->timer, ns_to_ktime(DUMMY_TIMER_INT_NSECS),
1315: HRTIMER_MODE_REL_SOFT);
1316: }
1317:
1318: done:
1319: spin_unlock_irqrestore(&dum_hcd->dum->lock, flags);
1320: return rc;
1321: }
SourceFile:drivers/usb/gadget/udc/dummy_hcd.c]
|
| 14/2 |
2026/05/16 14:10 |
llm |
debugger |
0m
Model:
Tokens:
input: 18393
output: 47
thoughts: 149
Thoughts: **Debugging a USB Driver Crash**
Okay, so I've hit a nasty crash, and the backtrace points directly at `list_add_tail(&urb->urb_list, &urb->ep->urb_list);` within `usb_hcd_link_urb_to_ep`. My immediate thought is, "Aha! That function is trying to add an URB to a list, but the error suggests that `urb->urb_list` is *already* on a list!". That's a classic double-link issue. Time to trace the URB's lifecycle.
Right, let me see. I remember `cm109_submit_buzz_toggle` submitting `dev->urb_ctl`. Okay, that makes sense for the control endpoint for the device. Now, if that control URB, `dev->urb_ctl`, is still submitted when `cm109_submit_buzz_toggle` gets called again, `usb_submit_urb` will get called for an URB that's already in the system. That's got to be it! It's likely trying to submit an already submitted URB.
Hang on a second… doesn't `usb_submit_urb` have a check for that? I thought it would prevent a double submission. Hmm. I’m clearly missing something here. Right, I need to verify what `usb_submit_urb` or `usb_hcd_submit_urb` actually do. Time to dive into those functions and confirm how they handle this scenario. Let's see if there's a race condition I'm not seeing, or if my initial assumption is wrong.
|
| 15/2 |
2026/05/16 14:10 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/core/hcd.c IncludeLines:true Name:usb_hcd_submit_urb]
Results: map[SourceCode:1510: /* may be called in any context with a valid urb->dev usecount
1511: * caller surrenders "ownership" of urb
1512: * expects usb_submit_urb() to have sanity checked and conditioned all
1513: * inputs in the urb
1514: */
1515: int usb_hcd_submit_urb (struct urb *urb, gfp_t mem_flags)
1516: {
1517: int status;
1518: struct usb_hcd *hcd = bus_to_hcd(urb->dev->bus);
1519:
1520: /* increment urb's reference count as part of giving it to the HCD
1521: * (which will control it). HCD guarantees that it either returns
1522: * an error or calls giveback(), but not both.
1523: */
1524: usb_get_urb(urb);
1525: atomic_inc(&urb->use_count);
1526: atomic_inc(&urb->dev->urbnum);
1527: usbmon_urb_submit(&hcd->self, urb);
1528:
1529: /* NOTE requirements on root-hub callers (usbfs and the hub
1530: * driver, for now): URBs' urb->transfer_buffer must be
1531: * valid and usb_buffer_{sync,unmap}() not be needed, since
1532: * they could clobber root hub response data. Also, control
1533: * URBs must be submitted in process context with interrupts
1534: * enabled.
1535: */
1536:
1537: if (is_root_hub(urb->dev)) {
1538: status = rh_urb_enqueue(hcd, urb);
1539: } else {
1540: status = map_urb_for_dma(hcd, urb, mem_flags);
1541: if (likely(status == 0)) {
1542: status = hcd->driver->urb_enqueue(hcd, urb, mem_flags);
1543: if (unlikely(status))
1544: unmap_urb_for_dma(hcd, urb);
1545: }
1546: }
1547:
1548: if (unlikely(status)) {
1549: usbmon_urb_submit_error(&hcd->self, urb, status);
1550: urb->hcpriv = NULL;
1551: INIT_LIST_HEAD(&urb->urb_list);
1552: atomic_dec(&urb->use_count);
1553: /*
1554: * Order the write of urb->use_count above before the read
1555: * of urb->reject below. Pairs with the memory barriers in
1556: * usb_kill_urb() and usb_poison_urb().
1557: */
1558: smp_mb__after_atomic();
1559:
1560: atomic_dec(&urb->dev->urbnum);
1561: if (atomic_read(&urb->reject))
1562: wake_up(&usb_kill_urb_queue);
1563: usb_put_urb(urb);
1564: }
1565: return status;
1566: }
SourceFile:drivers/usb/core/hcd.c]
|
| 16/2 |
2026/05/16 14:10 |
llm |
debugger |
0m
Model:
Tokens:
input: 19895
output: 43
thoughts: 38
Thoughts: Okay, here's my interpretation of those thoughts, summarized from an expert's perspective, presented as first-person internal monologue:
**URB Submission Workflow - A Quick Check**
Right, so I'm focusing on the `usb_submit_urb` function call. I know that's the entry point for getting a USB Request Block (URB) onto the bus. It's calling `usb_hcd_submit_urb`, which makes perfect sense; that's the Handset Controller Driver (HCD) interface, the low-level stuff we're interacting with. But, just a quick mental check... I need to make sure I haven't missed something fundamental. My key question is: does `usb_submit_urb` perform any pre-submission checks? Does it include a check to determine whether the URB has already been sent? This is a crucial area to avoid errors and race conditions. I need to make sure I understand the logic to prevent accidental double-submissions, as that can lead to all sorts of nasty side effects, like deadlocks or corruption of the USB controller state. I need to refresh my memory on what protection the kernel has for concurrent URB submission and completion.
|
| 17/2 |
2026/05/16 14:10 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/core/urb.c IncludeLines:true Name:usb_submit_urb]
Results: map[SourceCode: 230: /**
231: * usb_submit_urb - issue an asynchronous transfer request for an endpoint
232: * @urb: pointer to the urb describing the request
233: * @mem_flags: the type of memory to allocate, see kmalloc() for a list
234: * of valid options for this.
235: *
236: * This submits a transfer request, and transfers control of the URB
237: * describing that request to the USB subsystem. Request completion will
238: * be indicated later, asynchronously, by calling the completion handler.
239: * The three types of completion are success, error, and unlink
240: * (a software-induced fault, also called "request cancellation").
241: *
242: * URBs may be submitted in interrupt context.
243: *
244: * The caller must have correctly initialized the URB before submitting
245: * it. Functions such as usb_fill_bulk_urb() and usb_fill_control_urb() are
246: * available to ensure that most fields are correctly initialized, for
247: * the particular kind of transfer, although they will not initialize
248: * any transfer flags.
249: *
250: * If the submission is successful, the complete() callback from the URB
251: * will be called exactly once, when the USB core and Host Controller Driver
252: * (HCD) are finished with the URB. When the completion function is called,
253: * control of the URB is returned to the device driver which issued the
254: * request. The completion handler may then immediately free or reuse that
255: * URB.
256: *
257: * With few exceptions, USB device drivers should never access URB fields
258: * provided by usbcore or the HCD until its complete() is called.
259: * The exceptions relate to periodic transfer scheduling. For both
260: * interrupt and isochronous urbs, as part of successful URB submission
261: * urb->interval is modified to reflect the actual transfer period used
262: * (normally some power of two units). And for isochronous urbs,
263: * urb->start_frame is modified to reflect when the URB's transfers were
264: * scheduled to start.
265: *
266: * Not all isochronous transfer scheduling policies will work, but most
267: * host controller drivers should easily handle ISO queues going from now
268: * until 10-200 msec into the future. Drivers should try to keep at
269: * least one or two msec of data in the queue; many controllers require
270: * that new transfers start at least 1 msec in the future when they are
271: * added. If the driver is unable to keep up and the queue empties out,
272: * the behavior for new submissions is governed by the URB_ISO_ASAP flag.
273: * If the flag is set, or if the queue is idle, then the URB is always
274: * assigned to the first available (and not yet expired) slot in the
275: * endpoint's schedule. If the flag is not set and the queue is active
276: * then the URB is always assigned to the next slot in the schedule
277: * following the end of the endpoint's previous URB, even if that slot is
278: * in the past. When a packet is assigned in this way to a slot that has
279: * already expired, the packet is not transmitted and the corresponding
280: * usb_iso_packet_descriptor's status field will return -EXDEV. If this
281: * would happen to all the packets in the URB, submission fails with a
282: * -EXDEV error code.
283: *
284: * For control endpoints, the synchronous usb_control_msg() call is
285: * often used (in non-interrupt context) instead of this call.
286: * That is often used through convenience wrappers, for the requests
287: * that are standardized in the USB 2.0 specification. For bulk
288: * endpoints, a synchronous usb_bulk_msg() call is available.
289: *
290: * Return:
291: * 0 on successful submissions. A negative error number otherwise.
292: *
293: * Request Queuing:
294: *
295: * URBs may be submitted to endpoints before previous ones complete, to
296: * minimize the impact of interrupt latencies and system overhead on data
297: * throughput. With that queuing policy, an endpoint's queue would never
298: * be empty. This is required for continuous isochronous data streams,
299: * and may also be required for some kinds of interrupt transfers. Such
300: * queuing also maximizes bandwidth utilization by letting USB controllers
301: * start work on later requests before driver software has finished the
302: * completion processing for earlier (successful) requests.
303: *
304: * As of Linux 2.6, all USB endpoint transfer queues support depths greater
305: * than one. This was previously a HCD-specific behavior, except for ISO
306: * transfers. Non-isochronous endpoint queues are inactive during cleanup
307: * after faults (transfer errors or cancellation).
308: *
309: * Reserved Bandwidth Transfers:
310: *
311: * Periodic transfers (interrupt or isochronous) are performed repeatedly,
312: * using the interval specified in the urb. Submitting the first urb to
313: * the endpoint reserves the bandwidth necessary to make those transfers.
314: * If the USB subsystem can't allocate sufficient bandwidth to perform
315: * the periodic request, submitting such a periodic request should fail.
316: *
317: * For devices under xHCI, the bandwidth is reserved at configuration time, or
318: * when the alt setting is selected. If there is not enough bus bandwidth, the
319: * configuration/alt setting request will fail. Therefore, submissions to
320: * periodic endpoints on devices under xHCI should never fail due to bandwidth
321: * constraints.
322: *
323: * Device drivers must explicitly request that repetition, by ensuring that
324: * some URB is always on the endpoint's queue (except possibly for short
325: * periods during completion callbacks). When there is no longer an urb
326: * queued, the endpoint's bandwidth reservation is canceled. This means
327: * drivers can use their completion handlers to ensure they keep bandwidth
328: * they need, by reinitializing and resubmitting the just-completed urb
329: * until the driver longer needs that periodic bandwidth.
330: *
331: * Memory Flags:
332: *
333: * The general rules for how to decide which mem_flags to use
334: * are the same as for kmalloc. There are four
335: * different possible values; GFP_KERNEL, GFP_NOFS, GFP_NOIO and
336: * GFP_ATOMIC.
337: *
338: * GFP_NOFS is not ever used, as it has not been implemented yet.
339: *
340: * GFP_ATOMIC is used when
341: * (a) you are inside a completion handler, an interrupt, bottom half,
342: * tasklet or timer, or
343: * (b) you are holding a spinlock or rwlock (does not apply to
344: * semaphores), or
345: * (c) current->state != TASK_RUNNING, this is the case only after
346: * you've changed it.
347: *
348: * GFP_NOIO is used in the block io path and error handling of storage
349: * devices.
350: *
351: * All other situations use GFP_KERNEL.
352: *
353: * Some more specific rules for mem_flags can be inferred, such as
354: * (1) start_xmit, timeout, and receive methods of network drivers must
355: * use GFP_ATOMIC (they are called with a spinlock held);
356: * (2) queuecommand methods of scsi drivers must use GFP_ATOMIC (also
357: * called with a spinlock held);
358: * (3) If you use a kernel thread with a network driver you must use
359: * GFP_NOIO, unless (b) or (c) apply;
360: * (4) after you have done a down() you can use GFP_KERNEL, unless (b) or (c)
361: * apply or your are in a storage driver's block io path;
362: * (5) USB probe and disconnect can use GFP_KERNEL unless (b) or (c) apply; and
363: * (6) changing firmware on a running storage or net device uses
364: * GFP_NOIO, unless b) or c) apply
365: *
366: */
367: int usb_submit_urb(struct urb *urb, gfp_t mem_flags)
368: {
369: int xfertype, max;
370: struct usb_device *dev;
371: struct usb_host_endpoint *ep;
372: int is_out;
373: unsigned int allowed;
374: bool is_eusb2_isoch_double;
375:
376: if (!urb || !urb->complete)
377: return -EINVAL;
378: if (urb->hcpriv) {
379: WARN_ONCE(1, "URB %p submitted while active\n", urb);
380: return -EBUSY;
381: }
382:
383: dev = urb->dev;
384: if ((!dev) || (dev->state < USB_STATE_UNAUTHENTICATED))
385: return -ENODEV;
386:
387: /* For now, get the endpoint from the pipe. Eventually drivers
388: * will be required to set urb->ep directly and we will eliminate
389: * urb->pipe.
390: */
391: ep = usb_pipe_endpoint(dev, urb->pipe);
392: if (!ep)
393: return -ENOENT;
394:
395: urb->ep = ep;
396: urb->status = -EINPROGRESS;
397: urb->actual_length = 0;
398:
399: /* Lots of sanity checks, so HCDs can rely on clean data
400: * and don't need to duplicate tests
401: */
402: xfertype = usb_endpoint_type(&ep->desc);
403: if (xfertype == USB_ENDPOINT_XFER_CONTROL) {
404: struct usb_ctrlrequest *setup =
405: (struct usb_ctrlrequest *) urb->setup_packet;
406:
407: if (!setup)
408: return -ENOEXEC;
409: is_out = !(setup->bRequestType & USB_DIR_IN) ||
410: !setup->wLength;
411: dev_WARN_ONCE(&dev->dev, (usb_pipeout(urb->pipe) != is_out),
412: "BOGUS control dir, pipe %x doesn't match bRequestType %x\n",
413: urb->pipe, setup->bRequestType);
414: if (le16_to_cpu(setup->wLength) != urb->transfer_buffer_length) {
415: dev_dbg(&dev->dev, "BOGUS control len %d doesn't match transfer length %d\n",
416: le16_to_cpu(setup->wLength),
417: urb->transfer_buffer_length);
418: return -EBADR;
419: }
420: } else {
421: is_out = usb_endpoint_dir_out(&ep->desc);
422: }
423:
424: /* Clear the internal flags and cache the direction for later use */
425: urb->transfer_flags &= ~(URB_DIR_MASK | URB_DMA_MAP_SINGLE |
426: URB_DMA_MAP_PAGE | URB_DMA_MAP_SG | URB_MAP_LOCAL |
427: URB_SETUP_MAP_SINGLE | URB_SETUP_MAP_LOCAL |
428: URB_DMA_SG_COMBINED);
429: urb->transfer_flags |= (is_out ? URB_DIR_OUT : URB_DIR_IN);
430: kmsan_handle_urb(urb, is_out);
431:
432: if (xfertype != USB_ENDPOINT_XFER_CONTROL &&
433: dev->state < USB_STATE_CONFIGURED)
434: return -ENODEV;
435:
436: max = usb_endpoint_maxp(&ep->desc);
437: is_eusb2_isoch_double = usb_endpoint_is_hs_isoc_double(dev, ep);
438: if (!max && !is_eusb2_isoch_double) {
439: dev_dbg(&dev->dev,
440: "bogus endpoint ep%d%s in %s (bad maxpacket %d)\n",
441: usb_endpoint_num(&ep->desc), is_out ? "out" : "in",
442: __func__, max);
443: return -EMSGSIZE;
444: }
445:
446: /* periodic transfers limit size per frame/uframe,
447: * but drivers only control those sizes for ISO.
448: * while we're checking, initialize return status.
449: */
450: if (xfertype == USB_ENDPOINT_XFER_ISOC) {
451: int n, len;
452:
453: /* SuperSpeed isoc endpoints have up to 16 bursts of up to
454: * 3 packets each
455: */
456: if (dev->speed >= USB_SPEED_SUPER) {
457: int burst = 1 + ep->ss_ep_comp.bMaxBurst;
458: int mult = USB_SS_MULT(ep->ss_ep_comp.bmAttributes);
459: max *= burst;
460: max *= mult;
461: }
462:
463: if (dev->speed == USB_SPEED_SUPER_PLUS &&
464: USB_SS_SSP_ISOC_COMP(ep->ss_ep_comp.bmAttributes)) {
465: struct usb_ssp_isoc_ep_comp_descriptor *isoc_ep_comp;
466:
467: isoc_ep_comp = &ep->ssp_isoc_ep_comp;
468: max = le32_to_cpu(isoc_ep_comp->dwBytesPerInterval);
469: }
470:
471: /* High speed, 1-3 packets/uframe, max 6 for eUSB2 double bw */
472: if (dev->speed == USB_SPEED_HIGH) {
473: if (is_eusb2_isoch_double)
474: max = le32_to_cpu(ep->eusb2_isoc_ep_comp.dwBytesPerInterval);
475: else
476: max *= usb_endpoint_maxp_mult(&ep->desc);
477: }
478:
479: if (urb->number_of_packets <= 0)
480: return -EINVAL;
481: for (n = 0; n < urb->number_of_packets; n++) {
482: len = urb->iso_frame_desc[n].length;
483: if (len < 0 || len > max)
484: return -EMSGSIZE;
485: urb->iso_frame_desc[n].status = -EXDEV;
486: urb->iso_frame_desc[n].actual_length = 0;
487: }
488: } else if (urb->num_sgs && !urb->dev->bus->no_sg_constraint) {
489: struct scatterlist *sg;
490: int i;
491:
492: for_each_sg(urb->sg, sg, urb->num_sgs - 1, i)
493: if (sg->length % max)
494: return -EINVAL;
495: }
496:
497: /* the I/O buffer must be mapped/unmapped, except when length=0 */
498: if (urb->transfer_buffer_length > INT_MAX)
499: return -EMSGSIZE;
500:
501: /*
502: * stuff that drivers shouldn't do, but which shouldn't
503: * cause problems in HCDs if they get it wrong.
504: */
505:
506: /* Check that the pipe's type matches the endpoint's type */
507: if (usb_pipe_type_check(urb->dev, urb->pipe))
508: dev_warn_once(&dev->dev, "BOGUS urb xfer, pipe %x != type %x\n",
509: usb_pipetype(urb->pipe), pipetypes[xfertype]);
510:
511: /* Check against a simple/standard policy */
512: allowed = (URB_NO_TRANSFER_DMA_MAP | URB_NO_INTERRUPT | URB_DIR_MASK |
513: URB_FREE_BUFFER);
514: switch (xfertype) {
515: case USB_ENDPOINT_XFER_BULK:
516: case USB_ENDPOINT_XFER_INT:
517: if (is_out)
518: allowed |= URB_ZERO_PACKET;
519: fallthrough;
520: default: /* all non-iso endpoints */
521: if (!is_out)
522: allowed |= URB_SHORT_NOT_OK;
523: break;
524: case USB_ENDPOINT_XFER_ISOC:
525: allowed |= URB_ISO_ASAP;
526: break;
527: }
528: allowed &= urb->transfer_flags;
529:
530: /* warn if submitter gave bogus flags */
531: if (allowed != urb->transfer_flags)
532: dev_WARN(&dev->dev, "BOGUS urb flags, %x --> %x\n",
533: urb->transfer_flags, allowed);
534:
535: /*
536: * Force periodic transfer intervals to be legal values that are
537: * a power of two (so HCDs don't need to).
538: *
539: * FIXME want bus->{intr,iso}_sched_horizon values here. Each HC
540: * supports different values... this uses EHCI/UHCI defaults (and
541: * EHCI can use smaller non-default values).
542: */
543: switch (xfertype) {
544: case USB_ENDPOINT_XFER_ISOC:
545: case USB_ENDPOINT_XFER_INT:
546: /* too small? */
547: if (urb->interval <= 0)
548: return -EINVAL;
549:
550: /* too big? */
551: switch (dev->speed) {
552: case USB_SPEED_SUPER_PLUS:
553: case USB_SPEED_SUPER: /* units are 125us */
554: /* Handle up to 2^(16-1) microframes */
555: if (urb->interval > (1 << 15))
556: return -EINVAL;
557: max = 1 << 15;
558: break;
559: case USB_SPEED_HIGH: /* units are microframes */
560: /* NOTE usb handles 2^15 */
561: if (urb->interval > (1024 * 8))
562: urb->interval = 1024 * 8;
563: max = 1024 * 8;
564: break;
565: case USB_SPEED_FULL: /* units are frames/msec */
566: case USB_SPEED_LOW:
567: if (xfertype == USB_ENDPOINT_XFER_INT) {
568: if (urb->interval > 255)
569: return -EINVAL;
570: /* NOTE ohci only handles up to 32 */
571: max = 128;
572: } else {
573: if (urb->interval > 1024)
574: urb->interval = 1024;
575: /* NOTE usb and ohci handle up to 2^15 */
576: max = 1024;
577: }
578: break;
579: default:
580: return -EINVAL;
581: }
582: /* Round down to a power of 2, no more than max */
583: urb->interval = min(max, 1 << ilog2(urb->interval));
584: }
585:
586: return usb_hcd_submit_urb(urb, mem_flags);
587: }
SourceFile:drivers/usb/core/urb.c]
|
| 18/2 |
2026/05/16 14:10 |
llm |
debugger |
0m
Model:
Tokens:
input: 26463
output: 28
thoughts: 310
Thoughts: **Analyzing a Potential Race Condition in USB URB Handling**
Okay, so I'm digging into this `usb_submit_urb` function, and I see this check: `if (urb->hcpriv) { WARN_ONCE(1, "URB %p submitted while active\n", urb); return -EBUSY; }`. This immediately tells me that an URB is being submitted while it's already active, which is a big no-no. It's designed to prevent double submissions.
Then I trace `urb->hcpriv` being set. It looks like it's done in `dummy_urb_enqueue` with `urb->hcpriv = urbp;`. Makes sense, the driver is likely tracking its own private data using `hcpriv`.
Now I'm starting to think about concurrency. Let's see... what if `cm109_submit_buzz_toggle` gets called twice at the same time? Let me look at the code…Ah, there's `cm109_toggle_buzzer_async`, and it uses `guard(spinlock_irqsave)(&dev->ctl_submit_lock);`. That's good! That spinlock protects the section.
The code checks if `dev->ctl_urb_pending` is already set. If it *is*, it sets `dev->buzzer_pending` and lets the URB completion handler resubmit the URB. If it *isn't*, it sets `dev->ctl_urb_pending` to 1 and calls `cm109_submit_buzz_toggle(dev)`. That seems fairly safe… within the scope of that function.
But, wait a minute, I need to zoom out a bit. What if `cm109_submit_buzz_toggle` is also being called from *somewhere else*? If it's being called by another asynchronous process, is that safe? Is `dev->ctl_submit_lock` protecting the critical section for *all* possible callers? Is there a different lock being used elsewhere? I need to track down all call sites for `cm109_submit_buzz_toggle` to be sure. It could be possible to circumvent the protection if another function is calling it, and that function doesn't use the same spinlock. I have a feeling there might be a race condition here that needs further investigation.
|
| 19/2 |
2026/05/16 14:10 |
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]
|
| 20/2 |
2026/05/16 14:10 |
llm |
debugger |
0m
Model:
Tokens:
input: 27456
output: 51
thoughts: 16
Thoughts: Okay, let's dive into this. Here's my thought process on what we're looking at, summarized as though I'm thinking it:
**Analyzing `cm109_urb_ctl_callback`**
Right, let's take a look at this `cm109_urb_ctl_callback` function. It’s pretty standard practice to start with the core function involved. Understanding this callback is crucial for grasping how the system, likely a USB device driver, handles control transfers. I need to quickly recall the typical structure of these callbacks. Are we expecting an URB (USB Request Block) to have been completed here? What's the anticipated outcome? Is this a standard completion handler, or does it have some device-specific quirks?
I'll be looking to quickly identify the key actions within the function: error handling (of course!), data processing, resource release, and potentially the signaling of some kind of completion event to another part of the system. I also want to see if there is any indication of atomic operations or locking involved. Understanding the data flow and any dependencies is going to be important in establishing the function's overall purpose and function. Time to dig in!
|
| 21/2 |
2026/05/16 14:10 |
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]
|
| 22/2 |
2026/05/16 14:10 |
llm |
debugger |
0m
Model:
Tokens:
input: 28454
output: 21
thoughts: 1909
Thoughts: **Analyzing the Buzzer Toggle Logic**
Okay, let's break this down. My initial thought was that `cm109_urb_ctl_callback` is the crucial function, triggered when `urb_ctl` completes. It's supposed to reset `dev->ctl_urb_pending`. Then, based on `dev->buzzer_pending` and/or any error status, it'll resubmit, or not, `cm109_submit_buzz_toggle`. The first red flag is that if `status` is non-zero, it resubmits, meaning it thinks of an error as needing a retry, fair enough. But if the `usb_submit_urb` in `cm109_submit_buzz_toggle` fails, and it does, `dev->ctl_urb_pending` *stays* set, so a subsequent call to `cm109_toggle_buzzer_async` will just set `dev->buzzer_pending = 1`.
The bigger issue, though, is that the URB isn't *actually* submitted if `usb_submit_urb` fails. So, the toggle function sets `dev->buzzer_pending = 1`. But the URB *isn't* pending, so `cm109_urb_ctl_callback` will *never* be called. This means the buzzer gets stuck.
I dug into `cm109_submit_buzz_toggle` and confirmed that when `usb_submit_urb` fails, `dev->ctl_urb_pending` *remains* set to 1. Then `cm109_toggle_buzzer_async` is called, and if it sees `dev->ctl_urb_pending` set, it just sets `dev->buzzer_pending = 1` which is fine. This doesn't cause a double add of the URB.
Then, I went back to `cm109_urb_ctl_callback`. If that callback gets called with a non-zero `status`, it also leads to resubmitting. If that resubmit fails, again, `dev->ctl_urb_pending` remains 1.
The core question is, where does this "double add" come from? I went down the `usb_hcd_submit_urb` rabbit hole and checked `urb_enqueue`. If `urb_enqueue` (e.g., `dummy_urb_enqueue` in this case) fails, `usb_hcd_submit_urb` correctly resets the URB. But my concern was in the dummy HCD, what happens after `usb_hcd_link_urb_to_ep` and it's successful? It seems like `dummy_urb_enqueue` *always* succeeds after that point, it just returns a code.
Okay, what about the initial check by `usb_submit_urb` that I forgot. The function checks `if (urb->hcpriv) return -EBUSY;`. This looks promising... but the `urb->hcpriv` is cleared in `usb_hcd_submit_urb` if the submission *fails*. That means multiple threads would potentially not see the busy error, and possibly submit multiple URBs to the HCD. But, `dev->ctl_submit_lock` *should* protect against that in `cm109_toggle_buzzer_async` and `cm109_urb_ctl_callback`. `cm109_submit_buzz_toggle` is only called from those places.
|
| 23/2 |
2026/05/16 14:10 |
tool |
codesearch-find-references |
0m
Error:
Args:
Results: map[References:<nil> TruncatedOutput:false]
|
| 24/2 |
2026/05/16 14:10 |
llm |
debugger |
0m
Model:
Tokens:
input: 31168
output: 17
thoughts: 25
Thoughts: Okay, here's my take on those thoughts, framed as an expert's internal monologue:
**Investigating `urb_ctl` in the CM109 Driver**
Alright, time to get to the bottom of this. Let's start with the usual drill: I need to locate where `urb_ctl` is used. Given the context, my initial hunch is that it's likely to be in a driver related to input devices. So, `drivers/input` is a logical starting point. The specific target, `misc/cm109.c`, feels right. CM109 screams "HID-related device" to me, likely a USB-based gadget. This driver would probably be handling the low-level communication and data transfers with the device. `urb_ctl` has a very specific ring to it. This suggests some control or management functionality related to USB Request Blocks (URBs). I'm anticipating that it's probably orchestrating the submission, cancellation, or monitoring of URBs for interacting with the CM109 device. Let's see if my initial assessment is correct. Time to crack open that file and get to reading!
|
| 25/2 |
2026/05/16 14:10 |
tool |
grepper |
0m
Args:
Results: map[Output:drivers/input/misc/cm109.c=88=struct cm109_dev {
--
drivers/input/misc/cm109.c-101- struct usb_ctrlrequest *ctl_req;
drivers/input/misc/cm109.c:102: struct urb *urb_ctl;
drivers/input/misc/cm109.c-103- /*
--
drivers/input/misc/cm109.c=342=static void cm109_submit_buzz_toggle(struct cm109_dev *dev)
--
drivers/input/misc/cm109.c-350-
drivers/input/misc/cm109.c:351: error = usb_submit_urb(dev->urb_ctl, GFP_ATOMIC);
drivers/input/misc/cm109.c-352- if (error)
drivers/input/misc/cm109.c-353- dev_err(&dev->intf->dev,
drivers/input/misc/cm109.c:354: "%s: usb_submit_urb (urb_ctl) failed %d\n",
drivers/input/misc/cm109.c-355- __func__, error);
--
drivers/input/misc/cm109.c=358=static void cm109_submit_ctl(struct cm109_dev *dev)
--
drivers/input/misc/cm109.c-379-
drivers/input/misc/cm109.c:380: error = usb_submit_urb(dev->urb_ctl, GFP_ATOMIC);
drivers/input/misc/cm109.c-381- if (error)
drivers/input/misc/cm109.c-382- dev_err(&dev->intf->dev,
drivers/input/misc/cm109.c:383: "%s: usb_submit_urb (urb_ctl) failed %d\n",
drivers/input/misc/cm109.c-384- __func__, error);
--
drivers/input/misc/cm109.c=390=static void cm109_urb_irq_callback(struct urb *urb)
--
drivers/input/misc/cm109.c-433-
drivers/input/misc/cm109.c:434:static void cm109_urb_ctl_callback(struct urb *urb)
drivers/input/misc/cm109.c-435-{
--
drivers/input/misc/cm109.c=510=static void cm109_stop_traffic(struct cm109_dev *dev)
--
drivers/input/misc/cm109.c-517-
drivers/input/misc/cm109.c:518: usb_kill_urb(dev->urb_ctl);
drivers/input/misc/cm109.c-519- usb_kill_urb(dev->urb_irq);
--
drivers/input/misc/cm109.c=542=static int cm109_input_open(struct input_dev *idev)
--
drivers/input/misc/cm109.c-565- dev->ctl_urb_pending = 1;
drivers/input/misc/cm109.c:566: error = usb_submit_urb(dev->urb_ctl, GFP_KERNEL);
drivers/input/misc/cm109.c-567- if (!error) {
--
drivers/input/misc/cm109.c-575-
drivers/input/misc/cm109.c:576: dev_err(&dev->intf->dev, "%s: usb_submit_urb (urb_ctl) failed %d\n",
drivers/input/misc/cm109.c-577- __func__, error);
--
drivers/input/misc/cm109.c=657=static void cm109_usb_cleanup(struct cm109_dev *dev)
--
drivers/input/misc/cm109.c-663- usb_free_urb(dev->urb_irq); /* parameter validation in core/urb */
drivers/input/misc/cm109.c:664: usb_free_urb(dev->urb_ctl); /* parameter validation in core/urb */
drivers/input/misc/cm109.c-665- kfree(dev);
--
drivers/input/misc/cm109.c=677=static int cm109_usb_probe(struct usb_interface *intf,
--
drivers/input/misc/cm109.c-732-
drivers/input/misc/cm109.c:733: dev->urb_ctl = usb_alloc_urb(0, GFP_KERNEL);
drivers/input/misc/cm109.c:734: if (!dev->urb_ctl)
drivers/input/misc/cm109.c-735- goto err_out;
--
drivers/input/misc/cm109.c-759-
drivers/input/misc/cm109.c:760: usb_fill_control_urb(dev->urb_ctl, udev, usb_sndctrlpipe(udev, 0),
drivers/input/misc/cm109.c-761- (void *)dev->ctl_req, dev->ctl_data, USB_PKT_LEN,
drivers/input/misc/cm109.c:762: cm109_urb_ctl_callback, dev);
drivers/input/misc/cm109.c:763: dev->urb_ctl->transfer_dma = dev->ctl_dma;
drivers/input/misc/cm109.c:764: dev->urb_ctl->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
drivers/input/misc/cm109.c:765: dev->urb_ctl->dev = udev;
drivers/input/misc/cm109.c-766-
--
drivers/input/misc/yealink.c=85=struct yealink_dev {
--
drivers/input/misc/yealink.c-98- struct usb_ctrlrequest *ctl_req;
drivers/input/misc/yealink.c:99: struct urb *urb_ctl;
drivers/input/misc/yealink.c-100-
--
drivers/input/misc/yealink.c=412=static void urb_irq_callback(struct urb *urb)
--
drivers/input/misc/yealink.c-441- if (!yld->shutdown) {
drivers/input/misc/yealink.c:442: ret = usb_submit_urb(yld->urb_ctl, GFP_ATOMIC);
drivers/input/misc/yealink.c-443- if (ret && ret != -EPERM)
--
drivers/input/misc/yealink.c-449-
drivers/input/misc/yealink.c:450:static void urb_ctl_callback(struct urb *urb)
drivers/input/misc/yealink.c-451-{
--
drivers/input/misc/yealink.c-469- if (!yld->shutdown)
drivers/input/misc/yealink.c:470: ret = usb_submit_urb(yld->urb_ctl, GFP_ATOMIC);
drivers/input/misc/yealink.c-471- break;
--
drivers/input/misc/yealink.c=503=static int input_open(struct input_dev *dev)
--
drivers/input/misc/yealink.c-521- yld->ctl_data->sum = 0x100-CMD_INIT-10;
drivers/input/misc/yealink.c:522: if ((ret = usb_submit_urb(yld->urb_ctl, GFP_KERNEL)) != 0) {
drivers/input/misc/yealink.c-523- dev_dbg(&yld->intf->dev,
--
drivers/input/misc/yealink.c=531=static void input_close(struct input_dev *dev)
--
drivers/input/misc/yealink.c-541-
drivers/input/misc/yealink.c:542: usb_kill_urb(yld->urb_ctl);
drivers/input/misc/yealink.c-543- usb_kill_urb(yld->urb_irq);
--
drivers/input/misc/yealink.c=784=static int usb_cleanup(struct yealink_dev *yld, int err)
--
drivers/input/misc/yealink.c-796- usb_free_urb(yld->urb_irq);
drivers/input/misc/yealink.c:797: usb_free_urb(yld->urb_ctl);
drivers/input/misc/yealink.c-798-
--
drivers/input/misc/yealink.c=815=static int usb_probe(struct usb_interface *intf, const struct usb_device_id *id)
--
drivers/input/misc/yealink.c-865-
drivers/input/misc/yealink.c:866: yld->urb_ctl = usb_alloc_urb(0, GFP_KERNEL);
drivers/input/misc/yealink.c:867: if (yld->urb_ctl == NULL)
drivers/input/misc/yealink.c-868- return usb_cleanup(yld, -ENOMEM);
--
drivers/input/misc/yealink.c-893-
drivers/input/misc/yealink.c:894: usb_fill_control_urb(yld->urb_ctl, udev, usb_sndctrlpipe(udev, 0),
drivers/input/misc/yealink.c-895- (void *)yld->ctl_req, yld->ctl_data, USB_PKT_LEN,
drivers/input/misc/yealink.c:896: urb_ctl_callback, yld);
drivers/input/misc/yealink.c:897: yld->urb_ctl->transfer_dma = yld->ctl_dma;
drivers/input/misc/yealink.c:898: yld->urb_ctl->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
drivers/input/misc/yealink.c:899: yld->urb_ctl->dev = udev;
drivers/input/misc/yealink.c-900-
--
drivers/media/usb/stk1160/stk1160-v4l.c=203=static int stk1160_start_streaming(struct stk1160 *dev)
--
drivers/media/usb/stk1160/stk1160-v4l.c-234- for (i = 0; i < dev->isoc_ctl.num_bufs; i++) {
drivers/media/usb/stk1160/stk1160-v4l.c:235: rc = usb_submit_urb(dev->isoc_ctl.urb_ctl[i].urb, GFP_KERNEL);
drivers/media/usb/stk1160/stk1160-v4l.c-236- if (rc) {
--
drivers/media/usb/stk1160/stk1160-video.c=333=void stk1160_cancel_isoc(struct stk1160 *dev)
--
drivers/media/usb/stk1160/stk1160-video.c-352- */
drivers/media/usb/stk1160/stk1160-video.c:353: usb_kill_urb(dev->isoc_ctl.urb_ctl[i].urb);
drivers/media/usb/stk1160/stk1160-video.c-354- }
--
drivers/media/usb/stk1160/stk1160-video.c=377=void stk1160_free_isoc(struct stk1160 *dev)
--
drivers/media/usb/stk1160/stk1160-video.c-383- for (i = 0; i < num_bufs; i++)
drivers/media/usb/stk1160/stk1160-video.c:384: stk_free_urb(dev, &dev->isoc_ctl.urb_ctl[i]);
drivers/media/usb/stk1160/stk1160-video.c-385-
--
drivers/media/usb/stk1160/stk1160-video.c=431=int stk1160_alloc_isoc(struct stk1160 *dev)
--
drivers/media/usb/stk1160/stk1160-video.c-456-
drivers/media/usb/stk1160/stk1160-video.c:457: ret = stk1160_fill_urb(dev, &dev->isoc_ctl.urb_ctl[i],
drivers/media/usb/stk1160/stk1160-video.c-458- sb_size, max_packets);
--
drivers/media/usb/stk1160/stk1160-video.c-461-
drivers/media/usb/stk1160/stk1160-video.c:462: urb = dev->isoc_ctl.urb_ctl[i].urb;
drivers/media/usb/stk1160/stk1160-video.c-463-
--
drivers/media/usb/stk1160/stk1160-video.c-469- }
drivers/media/usb/stk1160/stk1160-video.c:470: memset(dev->isoc_ctl.urb_ctl[i].transfer_buffer, 0, sb_size);
drivers/media/usb/stk1160/stk1160-video.c-471-
--
drivers/media/usb/stk1160/stk1160-video.c-476- urb->pipe = usb_rcvisocpipe(dev->udev, STK1160_EP_VIDEO);
drivers/media/usb/stk1160/stk1160-video.c:477: urb->transfer_buffer = dev->isoc_ctl.urb_ctl[i].transfer_buffer;
drivers/media/usb/stk1160/stk1160-video.c-478- urb->transfer_buffer_length = sb_size;
--
drivers/media/usb/stk1160/stk1160-video.c-484- urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP;
drivers/media/usb/stk1160/stk1160-video.c:485: urb->transfer_dma = dev->isoc_ctl.urb_ctl[i].dma;
drivers/media/usb/stk1160/stk1160-video.c:486: urb->sgt = dev->isoc_ctl.urb_ctl[i].sgt;
drivers/media/usb/stk1160/stk1160-video.c-487-
--
drivers/media/usb/stk1160/stk1160.h=95=struct stk1160_isoc_ctl {
--
drivers/media/usb/stk1160/stk1160.h-101-
drivers/media/usb/stk1160/stk1160.h:102: struct stk1160_urb urb_ctl[STK1160_NUM_BUFS];
drivers/media/usb/stk1160/stk1160.h-103-
]
|
| 26/2 |
2026/05/16 14:10 |
llm |
debugger |
0m
Model:
Tokens:
input: 35594
output: 49
thoughts: 37
Thoughts: Okay, here's my take on those thoughts, framed for an expert audience:
**Analyzing the USB Control Transfer Submission**
Right, so `cm109_submit_ctl`... it's immediately apparent that it's responsible for submitting `dev->urb_ctl` as well. That's good to know, confirming my initial hunch about its core function. Now, I need to drill down into the internals of `cm109_submit_ctl`. The nuances of the control transfer setup and the interaction with the underlying USB stack are what I'm interested in understanding. I want to see how it's setting up the URB, preparing the data, handling any specific endpoint requirements, and, crucially, how it manages completion and error conditions. Understanding its synchronization strategy within the driver context will be key. Time to get into the details of that function.
|
| 27/2 |
2026/05/16 14:10 |
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]
|
| 28/2 |
2026/05/16 14:10 |
llm |
debugger |
0m
Model:
Tokens:
input: 36327
output: 26
thoughts: 13
Thoughts: Here's my thought process, as if I were considering this question:
**Tracing the Execution Path of `cm109_submit_ctl`**
Okay, so someone wants to know who's calling `cm109_submit_ctl`. That implies we're trying to understand the control flow, the dependencies, or perhaps even troubleshoot an issue related to this particular function. My initial approach would be, as always, to start with a systematic investigation, given that it's a function name.
First, I'd need to determine the codebase or scope we're talking about. Is this a custom library, a specific piece of proprietary software, or something within a commonly used framework? Knowing the context is key to focusing the search. I need to know where to begin my investigation.
Assuming I have access to the code, a straightforward grep or equivalent search tool is the obvious first step. I'd be looking for all instances of `cm109_submit_ctl` (and potentially variations like `Cm109SubmitCtl`, depending on the naming conventions in play). I'd pay close attention to the contexts surrounding each call: are they within the same module, across different modules, or maybe even in separate executables?
If grep doesn't yield a useful answer (and, in my experience, it often does in straightforward cases), I'd then turn to more sophisticated tools. A debugger, or a code analysis tool capable of call graph generation, would be next on the list. These tools provide a dynamic and more accurate view of the program's execution flow, helping to trace the calls from their origin.
When investigating a function call, I also need to consider indirect calls. Is `cm109_submit_ctl` a function pointer assigned in a data structure, or part of a callback mechanism? I'd have to identify all variables and data structures containing function pointers, or any API using callback registration.
Beyond that, I'd need to consider where the program starts. Is `cm109_submit_ctl` called in a thread, or is it event driven? How does it receive input? Is this triggered directly by a user command, a scheduled job, a network request, or based on system events?
Finally, if the call is conditional, I will need to understand the conditions triggering it. Is it dependent on a particular state variable, the outcome of another function, or an environmental factor? I'd analyze the surrounding code to determine these dependencies. Ultimately, the goal is to create a clear and complete picture of the execution paths that lead to the execution of `cm109_submit_ctl`.
|
| 29/2 |
2026/05/16 14:10 |
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]
|
| 30/2 |
2026/05/16 14:10 |
llm |
debugger |
0m
Model:
Tokens:
input: 37003
output: 52
thoughts: 17
Thoughts: Okay, here's my take on those thoughts, framed as an expert's internal monologue:
**Diving into the `cm109_urb_irq_callback`**
Right, let's take a look at `cm109_urb_irq_callback`. Seems like we're troubleshooting something related to interrupt handling within the USB subsystem, likely at the driver level for a specific device, the `cm109`. The "URB" hints at this being tied to a USB Request Block, meaning we're dealing with data transfers and their completion. My first thought is to understand the context: what's the broader issue this callback is addressing? Is it performance-related, an outright failure to receive data, or maybe an unexpected interrupt triggering? The specific error reporting within the callback itself will be key. We'll need to confirm what the interrupt is signaling, what data is supposed to be present, and how that data is being processed once the interrupt fires. Potential areas of interest are: error checking on the URB completion codes, handling of the data buffer pointer, synchronization with other parts of the driver, and potential race conditions. We'll examine the surrounding code to understand the logic flow, and the timing of each step in the function.
|
| 31/2 |
2026/05/16 14:10 |
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]
|
| 32/2 |
2026/05/16 14:10 |
llm |
debugger |
0m
Model:
Tokens:
input: 38090
output: 48
thoughts: 536
Thoughts: **My Analysis of the `cm109_submit_ctl` Function and Potential USB URB Submissions**
Okay, so I'm looking at this `cm109_urb_irq_callback` function, and it looks like it calls `cm109_submit_ctl(dev)`. Let's see what that function does. Ah, I see it's using a `ctl_submit_lock` spinlock, which is good for concurrency. It then sets `dev->irq_urb_pending = 0`. That's reasonable. It checks for a shutdown condition and then manipulates some control data bytes based on `dev->buzzer_state` and `dev->keybit`, all standard stuff for setting up the device's output.
But here's where it gets interesting. I see that `dev->ctl_urb_pending` is set to 1 *unconditionally*. It doesn't check if it was already 1! That's a red flag. If it was already 1, that means `dev->urb_ctl` has already been submitted and is pending. And right after that, it calls `usb_submit_urb(dev->urb_ctl, GFP_ATOMIC)`. Now, if `dev->ctl_urb_pending` was already 1, this means it's submitting the same URB *again* while it's already pending! That sounds dangerous.
The immediate thought is that `usb_submit_urb` should protect against this double-submission by returning `-EBUSY`. Okay, good. But I'm starting to worry about this. I've got to consider all the scenarios here.
Hold on, I need to understand when `usb_submit_urb` actually returns `-EBUSY`. It checks for `urb->hcpriv`, right? What if that field *isn't* set yet? Or what if it's cleared prematurely?
Okay, so looking at the code for `usb_hcd_submit_urb`, it sets `urb->hcpriv = urbp`. Now I remember: it looks like in `usb_hcd_submit_urb` that it's cleared when the status is non-zero. Also, `usb_hcd_giveback_urb` can also clear it. I really need to check if there are any race conditions based on when `hcpriv` is cleared.
So, in the best case, `usb_submit_urb` returns -EBUSY and everything's fine. Worst case, there's a race condition and the URB is submitted twice, leading to who knows what kind of havoc.
|
| 33/2 |
2026/05/16 14:10 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/core/hcd.c IncludeLines:true Name:usb_hcd_giveback_urb]
Results: map[SourceCode:1710: /**
1711: * usb_hcd_giveback_urb - return URB from HCD to device driver
1712: * @hcd: host controller returning the URB
1713: * @urb: urb being returned to the USB device driver.
1714: * @status: completion status code for the URB.
1715: *
1716: * Context: atomic. The completion callback is invoked either in a work queue
1717: * (BH) context or in the caller's context, depending on whether the HCD_BH
1718: * flag is set in the @hcd structure, except that URBs submitted to the
1719: * root hub always complete in BH context.
1720: *
1721: * This hands the URB from HCD to its USB device driver, using its
1722: * completion function. The HCD has freed all per-urb resources
1723: * (and is done using urb->hcpriv). It also released all HCD locks;
1724: * the device driver won't cause problems if it frees, modifies,
1725: * or resubmits this URB.
1726: *
1727: * If @urb was unlinked, the value of @status will be overridden by
1728: * @urb->unlinked. Erroneous short transfers are detected in case
1729: * the HCD hasn't checked for them.
1730: */
1731: void usb_hcd_giveback_urb(struct usb_hcd *hcd, struct urb *urb, int status)
1732: {
1733: struct giveback_urb_bh *bh;
1734: bool running;
1735:
1736: /* pass status to BH via unlinked */
1737: if (likely(!urb->unlinked))
1738: urb->unlinked = status;
1739:
1740: if (!hcd_giveback_urb_in_bh(hcd) && !is_root_hub(urb->dev)) {
1741: __usb_hcd_giveback_urb(urb);
1742: return;
1743: }
1744:
1745: if (usb_pipeisoc(urb->pipe) || usb_pipeint(urb->pipe))
1746: bh = &hcd->high_prio_bh;
1747: else
1748: bh = &hcd->low_prio_bh;
1749:
1750: spin_lock(&bh->lock);
1751: list_add_tail(&urb->urb_list, &bh->head);
1752: running = bh->running;
1753: spin_unlock(&bh->lock);
1754:
1755: if (running)
1756: ;
1757: else if (bh->high_prio)
1758: queue_work(system_bh_highpri_wq, &bh->bh);
1759: else
1760: queue_work(system_bh_wq, &bh->bh);
1761: }
SourceFile:drivers/usb/core/hcd.c]
|
| 34/2 |
2026/05/16 14:10 |
llm |
debugger |
0m
Model:
Tokens:
input: 40141
output: 48
thoughts: 225
Thoughts: **Double-Linked URB Panic! (Or Maybe Not...)**
Okay, hold on a second. I need to think this through. `usb_hcd_giveback_urb` is doing something… it's putting the URB on `bh->head` using `list_add_tail`. Fine, that's normal. But now I'm starting to see the potential for a problem. If `usb_submit_urb` is called *while* that URB is still hanging out on `bh->head`, `usb_submit_urb` will trigger a chain of calls. It'll go through `usb_hcd_submit_urb`, then `hcd->driver->urb_enqueue`, which finally ends up in `usb_hcd_link_urb_to_ep`.
And *that's* where I'm getting worried! `usb_hcd_link_urb_to_ep` does another `list_add_tail`! It's adding `urb->urb_list` to `urb->ep->urb_list`. Double linking! We'd have the same URB's list entry added to two different lists which would cause serious issues! I need to be sure the URB isn't still on both.
But… wait, before I completely freak out, doesn't `usb_submit_urb` have a check for `urb->hcpriv`? Ah hah! That's it! If `urb->hcpriv` is set it won't submit. So the question is: when is `urb->hcpriv` cleared? I need to check when `hcpriv` is cleared! Specifically, I'm going to look at the `dummy_timer` or `dummy_urb_enqueue` functions to see how they handle that field. Gotta make sure `hcpriv` gets cleared *before* `usb_submit_urb` can even be called again on this URB while its still being processed to avoid this possible double-linked mess!
|
| 35/2 |
2026/05/16 14:10 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/gadget/udc/dummy_hcd.c IncludeLines:true Name:dummy_timer]
Results: map[SourceCode:1793: /*
1794: * Drive both sides of the transfers; looks like irq handlers to both
1795: * drivers except that the callbacks are invoked from soft interrupt
1796: * context.
1797: */
1798: static enum hrtimer_restart dummy_timer(struct hrtimer *t)
1799: {
1800: struct dummy_hcd *dum_hcd = timer_container_of(dum_hcd, t,
1801: timer);
1802: struct dummy *dum = dum_hcd->dum;
1803: struct urbp *urbp, *tmp;
1804: unsigned long flags;
1805: int limit, total;
1806: int i;
1807:
1808: /* simplistic model for one frame's bandwidth */
1809: /* FIXME: account for transaction and packet overhead */
1810: switch (dum->gadget.speed) {
1811: case USB_SPEED_LOW:
1812: total = 8/*bytes*/ * 12/*packets*/;
1813: break;
1814: case USB_SPEED_FULL:
1815: total = 64/*bytes*/ * 19/*packets*/;
1816: break;
1817: case USB_SPEED_HIGH:
1818: total = 512/*bytes*/ * 13/*packets*/ * 8/*uframes*/;
1819: break;
1820: case USB_SPEED_SUPER:
1821: /* Bus speed is 500000 bytes/ms, so use a little less */
1822: total = 490000;
1823: break;
1824: default: /* Can't happen */
1825: dev_err(dummy_dev(dum_hcd), "bogus device speed\n");
1826: total = 0;
1827: break;
1828: }
1829:
1830: /* look at each urb queued by the host side driver */
1831: spin_lock_irqsave(&dum->lock, flags);
1832: dum_hcd->timer_pending = 0;
1833:
1834: if (!dum_hcd->udev) {
1835: dev_err(dummy_dev(dum_hcd),
1836: "timer fired with no URBs pending?\n");
1837: spin_unlock_irqrestore(&dum->lock, flags);
1838: return HRTIMER_NORESTART;
1839: }
1840: dum_hcd->next_frame_urbp = NULL;
1841:
1842: for (i = 0; i < DUMMY_ENDPOINTS; i++) {
1843: if (!ep_info[i].name)
1844: break;
1845: dum->ep[i].already_seen = 0;
1846: }
1847:
1848: restart:
1849: list_for_each_entry_safe(urbp, tmp, &dum_hcd->urbp_list, urbp_list) {
1850: struct urb *urb;
1851: struct dummy_request *req;
1852: u8 address;
1853: struct dummy_ep *ep = NULL;
1854: int status = -EINPROGRESS;
1855:
1856: /* stop when we reach URBs queued after the timer interrupt */
1857: if (urbp == dum_hcd->next_frame_urbp)
1858: break;
1859:
1860: urb = urbp->urb;
1861: if (urb->unlinked)
1862: goto return_urb;
1863: else if (dum_hcd->rh_state != DUMMY_RH_RUNNING)
1864: continue;
1865:
1866: /* Used up this frame's bandwidth? */
1867: if (total <= 0)
1868: continue;
1869:
1870: /* find the gadget's ep for this request (if configured) */
1871: address = usb_pipeendpoint (urb->pipe);
1872: if (usb_urb_dir_in(urb))
1873: address |= USB_DIR_IN;
1874: ep = find_endpoint(dum, address);
1875: if (!ep) {
1876: /* set_configuration() disagreement */
1877: dev_dbg(dummy_dev(dum_hcd),
1878: "no ep configured for urb %p\n",
1879: urb);
1880: status = -EPROTO;
1881: goto return_urb;
1882: }
1883:
1884: if (ep->already_seen)
1885: continue;
1886: ep->already_seen = 1;
1887: if (ep == &dum->ep[0] && urb->error_count) {
1888: ep->setup_stage = 1; /* a new urb */
1889: urb->error_count = 0;
1890: }
1891: if (ep->halted && !ep->setup_stage) {
1892: /* NOTE: must not be iso! */
1893: dev_dbg(dummy_dev(dum_hcd), "ep %s halted, urb %p\n",
1894: ep->ep.name, urb);
1895: status = -EPIPE;
1896: goto return_urb;
1897: }
1898: /* FIXME make sure both ends agree on maxpacket */
1899:
1900: /* handle control requests */
1901: if (ep == &dum->ep[0] && ep->setup_stage) {
1902: struct usb_ctrlrequest setup;
1903: int value;
1904:
1905: setup = *(struct usb_ctrlrequest *) urb->setup_packet;
1906: /* paranoia, in case of stale queued data */
1907: list_for_each_entry(req, &ep->queue, queue) {
1908: list_del_init(&req->queue);
1909: req->req.status = -EOVERFLOW;
1910: dev_dbg(udc_dev(dum), "stale req = %p\n",
1911: req);
1912:
1913: spin_unlock(&dum->lock);
1914: usb_gadget_giveback_request(&ep->ep, &req->req);
1915: spin_lock(&dum->lock);
1916: ep->already_seen = 0;
1917: goto restart;
1918: }
1919:
1920: /* gadget driver never sees set_address or operations
1921: * on standard feature flags. some hardware doesn't
1922: * even expose them.
1923: */
1924: ep->last_io = jiffies;
1925: ep->setup_stage = 0;
1926: ep->halted = 0;
1927:
1928: value = handle_control_request(dum_hcd, urb, &setup,
1929: &status);
1930:
1931: /* gadget driver handles all other requests. block
1932: * until setup() returns; no reentrancy issues etc.
1933: */
1934: if (value > 0) {
1935: ++dum->callback_usage;
1936: spin_unlock(&dum->lock);
1937: value = dum->driver->setup(&dum->gadget,
1938: &setup);
1939: spin_lock(&dum->lock);
1940: --dum->callback_usage;
1941:
1942: if (value >= 0) {
1943: /* no delays (max 64KB data stage) */
1944: limit = 64*1024;
1945: goto treat_control_like_bulk;
1946: }
1947: /* error, see below */
1948: }
1949:
1950: if (value < 0) {
1951: if (value != -EOPNOTSUPP)
1952: dev_dbg(udc_dev(dum),
1953: "setup --> %d\n",
1954: value);
1955: status = -EPIPE;
1956: urb->actual_length = 0;
1957: }
1958:
1959: goto return_urb;
1960: }
1961:
1962: /* non-control requests */
1963: limit = total;
1964: switch (usb_pipetype(urb->pipe)) {
1965: case PIPE_ISOCHRONOUS:
1966: /*
1967: * We don't support isochronous. But if we did,
1968: * here are some of the issues we'd have to face:
1969: *
1970: * Is it urb->interval since the last xfer?
1971: * Use urb->iso_frame_desc[i].
1972: * Complete whether or not ep has requests queued.
1973: * Report random errors, to debug drivers.
1974: */
1975: limit = max(limit, periodic_bytes(dum, ep));
1976: status = -EINVAL; /* fail all xfers */
1977: break;
1978:
1979: case PIPE_INTERRUPT:
1980: /* FIXME is it urb->interval since the last xfer?
1981: * this almost certainly polls too fast.
1982: */
1983: limit = max(limit, periodic_bytes(dum, ep));
1984: fallthrough;
1985:
1986: default:
1987: treat_control_like_bulk:
1988: ep->last_io = jiffies;
1989: total -= transfer(dum_hcd, urb, ep, limit, &status);
1990: break;
1991: }
1992:
1993: /* incomplete transfer? */
1994: if (status == -EINPROGRESS)
1995: continue;
1996:
1997: return_urb:
1998: list_del(&urbp->urbp_list);
1999: kfree(urbp);
2000: if (ep)
2001: ep->already_seen = ep->setup_stage = 0;
2002:
2003: usb_hcd_unlink_urb_from_ep(dummy_hcd_to_hcd(dum_hcd), urb);
2004: spin_unlock(&dum->lock);
2005: usb_hcd_giveback_urb(dummy_hcd_to_hcd(dum_hcd), urb, status);
2006: spin_lock(&dum->lock);
2007:
2008: goto restart;
2009: }
2010:
2011: if (list_empty(&dum_hcd->urbp_list)) {
2012: usb_put_dev(dum_hcd->udev);
2013: dum_hcd->udev = NULL;
2014: } else if (!dum_hcd->timer_pending &&
2015: dum_hcd->rh_state == DUMMY_RH_RUNNING) {
2016: /* want a 1 msec delay here */
2017: dum_hcd->timer_pending = 1;
2018: hrtimer_start(&dum_hcd->timer, ns_to_ktime(DUMMY_TIMER_INT_NSECS),
2019: HRTIMER_MODE_REL_SOFT);
2020: }
2021:
2022: spin_unlock_irqrestore(&dum->lock, flags);
2023:
2024: return HRTIMER_NORESTART;
2025: }
SourceFile:drivers/usb/gadget/udc/dummy_hcd.c]
|
| 36/2 |
2026/05/16 14:10 |
llm |
debugger |
0m
Model:
Tokens:
input: 44341
output: 49
thoughts: 221
Thoughts: **Analyzing the `dummy_timer` Function**
Okay, so I'm looking at this `dummy_timer` function, and my initial thought is, "Okay, what's going on here?" Let's break it down: It appears to be doing some cleanup after a USB transfer. I see it's removing an entry from a list (`urbp->urbp_list`), freeing allocated memory (`kfree(urbp)`), and potentially resetting some endpoint flags (`ep->already_seen`, `ep->setup_stage`). It then calls `usb_hcd_unlink_urb_from_ep`, which makes sense for removing the Urban from the USB Host Controller Driver (HCD). Finally, it releases a spinlock (`dum->lock`), calls `usb_hcd_giveback_urb` to complete the transfer, and then reacquires the lock.
Now, wait a minute... something isn't sitting right. I'm focusing on the interaction with the `urb` (USB Request Block) here. Specifically, I'm thinking about `urb->hcpriv`. The code *doesn't* seem to explicitly clear `urb->hcpriv` within `dummy_timer`. That's important!
However, let me not jump to conclusions... I need to consider all the pieces. I recall that `usb_hcd_giveback_urb` is called immediately afterward. So perhaps `urb->hcpriv` is handled *there*. I need to confirm... I seem to recall that it calls `__usb_hcd_giveback_urb` (if not in a bottom half or BH). Ah, okay, time to double-check that `__usb_hcd_giveback_urb` function to see what exactly it's doing with `urb->hcpriv`. I need to make sure this isn't creating a race condition or causing a leak. Let's see...
|
| 37/2 |
2026/05/16 14:10 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/core/hcd.c IncludeLines:true Name:__usb_hcd_giveback_urb]
Results: map[SourceCode:1630: static void __usb_hcd_giveback_urb(struct urb *urb)
1631: {
1632: struct usb_hcd *hcd = bus_to_hcd(urb->dev->bus);
1633: struct usb_anchor *anchor = urb->anchor;
1634: int status = urb->unlinked;
1635:
1636: urb->hcpriv = NULL;
1637: if (unlikely((urb->transfer_flags & URB_SHORT_NOT_OK) &&
1638: urb->actual_length < urb->transfer_buffer_length &&
1639: !status))
1640: status = -EREMOTEIO;
1641:
1642: unmap_urb_for_dma(hcd, urb);
1643: usbmon_urb_complete(&hcd->self, urb, status);
1644: usb_anchor_suspend_wakeups(anchor);
1645: usb_unanchor_urb(urb);
1646: if (likely(status == 0))
1647: usb_led_activity(USB_LED_EVENT_HOST);
1648:
1649: /* pass ownership to the completion handler */
1650: urb->status = status;
1651: /*
1652: * This function can be called in task context inside another remote
1653: * coverage collection section, but kcov doesn't support that kind of
1654: * recursion yet. Only collect coverage in softirq context for now.
1655: */
1656: kcov_remote_start_usb_softirq((u64)urb->dev->bus->busnum);
1657: urb->complete(urb);
1658: kcov_remote_stop_softirq();
1659:
1660: usb_anchor_resume_wakeups(anchor);
1661: atomic_dec(&urb->use_count);
1662: /*
1663: * Order the write of urb->use_count above before the read
1664: * of urb->reject below. Pairs with the memory barriers in
1665: * usb_kill_urb() and usb_poison_urb().
1666: */
1667: smp_mb__after_atomic();
1668:
1669: if (unlikely(atomic_read(&urb->reject)))
1670: wake_up(&usb_kill_urb_queue);
1671: usb_put_urb(urb);
1672: }
SourceFile:drivers/usb/core/hcd.c]
|
| 38/2 |
2026/05/16 14:10 |
llm |
debugger |
0m
Model:
Tokens:
input: 45753
output: 43
thoughts: 426
Thoughts: **Analyzing the USB URB Callback Flow**
Okay, let's break this down. My initial observation is that `__usb_hcd_giveback_urb` is setting `urb->hcpriv = NULL;` and then immediately invoking the `urb->complete(urb)` callback. This means that by the time `cm109_urb_ctl_callback` is executed, `urb->hcpriv` is *guaranteed* to be NULL. I need to make a note of this.
Now, if `urb->hcpriv` is NULL, that means `usb_submit_urb` shouldn't return `-EBUSY`. This is different from my original understanding. But hang on, the crucial piece is that `cm109_urb_ctl_callback` is directly called *from* `__usb_hcd_giveback_urb`, where `urb->hcpriv` is already NULL. Then, within `cm109_urb_ctl_callback`, we see `cm109_submit_buzz_toggle(dev)` which calls `usb_submit_urb(dev->urb_ctl, GFP_ATOMIC)`. That makes sense - it's resubmitting the URB after completion.
But I'm starting to see a potential problem with `cm109_urb_irq_callback`. This callback is triggered when `dev->urb_irq` finishes. Within that callback, `urb` is `dev->urb_irq`, and then it calls `cm109_submit_ctl(dev)`. `cm109_submit_ctl` *also* calls `usb_submit_urb(dev->urb_ctl, GFP_ATOMIC)`. Here's where it gets interesting: what if `dev->urb_ctl` is already submitted and pending? If `dev->urb_ctl` is pending, its `hcpriv` *won't* be NULL. In that scenario, `usb_submit_urb` *will* return `-EBUSY`. So, in a way, it is protected from double-adding...
So, if it returns `-EBUSY`, it doesn't duplicate the submit, even though the state flow looks like it might. Just to be completely certain, I will check `usb_submit_urb` again.
|
| 39/2 |
2026/05/16 14:10 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/core/urb.c IncludeLines:true Name:usb_submit_urb]
Results: map[SourceCode: 230: /**
231: * usb_submit_urb - issue an asynchronous transfer request for an endpoint
232: * @urb: pointer to the urb describing the request
233: * @mem_flags: the type of memory to allocate, see kmalloc() for a list
234: * of valid options for this.
235: *
236: * This submits a transfer request, and transfers control of the URB
237: * describing that request to the USB subsystem. Request completion will
238: * be indicated later, asynchronously, by calling the completion handler.
239: * The three types of completion are success, error, and unlink
240: * (a software-induced fault, also called "request cancellation").
241: *
242: * URBs may be submitted in interrupt context.
243: *
244: * The caller must have correctly initialized the URB before submitting
245: * it. Functions such as usb_fill_bulk_urb() and usb_fill_control_urb() are
246: * available to ensure that most fields are correctly initialized, for
247: * the particular kind of transfer, although they will not initialize
248: * any transfer flags.
249: *
250: * If the submission is successful, the complete() callback from the URB
251: * will be called exactly once, when the USB core and Host Controller Driver
252: * (HCD) are finished with the URB. When the completion function is called,
253: * control of the URB is returned to the device driver which issued the
254: * request. The completion handler may then immediately free or reuse that
255: * URB.
256: *
257: * With few exceptions, USB device drivers should never access URB fields
258: * provided by usbcore or the HCD until its complete() is called.
259: * The exceptions relate to periodic transfer scheduling. For both
260: * interrupt and isochronous urbs, as part of successful URB submission
261: * urb->interval is modified to reflect the actual transfer period used
262: * (normally some power of two units). And for isochronous urbs,
263: * urb->start_frame is modified to reflect when the URB's transfers were
264: * scheduled to start.
265: *
266: * Not all isochronous transfer scheduling policies will work, but most
267: * host controller drivers should easily handle ISO queues going from now
268: * until 10-200 msec into the future. Drivers should try to keep at
269: * least one or two msec of data in the queue; many controllers require
270: * that new transfers start at least 1 msec in the future when they are
271: * added. If the driver is unable to keep up and the queue empties out,
272: * the behavior for new submissions is governed by the URB_ISO_ASAP flag.
273: * If the flag is set, or if the queue is idle, then the URB is always
274: * assigned to the first available (and not yet expired) slot in the
275: * endpoint's schedule. If the flag is not set and the queue is active
276: * then the URB is always assigned to the next slot in the schedule
277: * following the end of the endpoint's previous URB, even if that slot is
278: * in the past. When a packet is assigned in this way to a slot that has
279: * already expired, the packet is not transmitted and the corresponding
280: * usb_iso_packet_descriptor's status field will return -EXDEV. If this
281: * would happen to all the packets in the URB, submission fails with a
282: * -EXDEV error code.
283: *
284: * For control endpoints, the synchronous usb_control_msg() call is
285: * often used (in non-interrupt context) instead of this call.
286: * That is often used through convenience wrappers, for the requests
287: * that are standardized in the USB 2.0 specification. For bulk
288: * endpoints, a synchronous usb_bulk_msg() call is available.
289: *
290: * Return:
291: * 0 on successful submissions. A negative error number otherwise.
292: *
293: * Request Queuing:
294: *
295: * URBs may be submitted to endpoints before previous ones complete, to
296: * minimize the impact of interrupt latencies and system overhead on data
297: * throughput. With that queuing policy, an endpoint's queue would never
298: * be empty. This is required for continuous isochronous data streams,
299: * and may also be required for some kinds of interrupt transfers. Such
300: * queuing also maximizes bandwidth utilization by letting USB controllers
301: * start work on later requests before driver software has finished the
302: * completion processing for earlier (successful) requests.
303: *
304: * As of Linux 2.6, all USB endpoint transfer queues support depths greater
305: * than one. This was previously a HCD-specific behavior, except for ISO
306: * transfers. Non-isochronous endpoint queues are inactive during cleanup
307: * after faults (transfer errors or cancellation).
308: *
309: * Reserved Bandwidth Transfers:
310: *
311: * Periodic transfers (interrupt or isochronous) are performed repeatedly,
312: * using the interval specified in the urb. Submitting the first urb to
313: * the endpoint reserves the bandwidth necessary to make those transfers.
314: * If the USB subsystem can't allocate sufficient bandwidth to perform
315: * the periodic request, submitting such a periodic request should fail.
316: *
317: * For devices under xHCI, the bandwidth is reserved at configuration time, or
318: * when the alt setting is selected. If there is not enough bus bandwidth, the
319: * configuration/alt setting request will fail. Therefore, submissions to
320: * periodic endpoints on devices under xHCI should never fail due to bandwidth
321: * constraints.
322: *
323: * Device drivers must explicitly request that repetition, by ensuring that
324: * some URB is always on the endpoint's queue (except possibly for short
325: * periods during completion callbacks). When there is no longer an urb
326: * queued, the endpoint's bandwidth reservation is canceled. This means
327: * drivers can use their completion handlers to ensure they keep bandwidth
328: * they need, by reinitializing and resubmitting the just-completed urb
329: * until the driver longer needs that periodic bandwidth.
330: *
331: * Memory Flags:
332: *
333: * The general rules for how to decide which mem_flags to use
334: * are the same as for kmalloc. There are four
335: * different possible values; GFP_KERNEL, GFP_NOFS, GFP_NOIO and
336: * GFP_ATOMIC.
337: *
338: * GFP_NOFS is not ever used, as it has not been implemented yet.
339: *
340: * GFP_ATOMIC is used when
341: * (a) you are inside a completion handler, an interrupt, bottom half,
342: * tasklet or timer, or
343: * (b) you are holding a spinlock or rwlock (does not apply to
344: * semaphores), or
345: * (c) current->state != TASK_RUNNING, this is the case only after
346: * you've changed it.
347: *
348: * GFP_NOIO is used in the block io path and error handling of storage
349: * devices.
350: *
351: * All other situations use GFP_KERNEL.
352: *
353: * Some more specific rules for mem_flags can be inferred, such as
354: * (1) start_xmit, timeout, and receive methods of network drivers must
355: * use GFP_ATOMIC (they are called with a spinlock held);
356: * (2) queuecommand methods of scsi drivers must use GFP_ATOMIC (also
357: * called with a spinlock held);
358: * (3) If you use a kernel thread with a network driver you must use
359: * GFP_NOIO, unless (b) or (c) apply;
360: * (4) after you have done a down() you can use GFP_KERNEL, unless (b) or (c)
361: * apply or your are in a storage driver's block io path;
362: * (5) USB probe and disconnect can use GFP_KERNEL unless (b) or (c) apply; and
363: * (6) changing firmware on a running storage or net device uses
364: * GFP_NOIO, unless b) or c) apply
365: *
366: */
367: int usb_submit_urb(struct urb *urb, gfp_t mem_flags)
368: {
369: int xfertype, max;
370: struct usb_device *dev;
371: struct usb_host_endpoint *ep;
372: int is_out;
373: unsigned int allowed;
374: bool is_eusb2_isoch_double;
375:
376: if (!urb || !urb->complete)
377: return -EINVAL;
378: if (urb->hcpriv) {
379: WARN_ONCE(1, "URB %p submitted while active\n", urb);
380: return -EBUSY;
381: }
382:
383: dev = urb->dev;
384: if ((!dev) || (dev->state < USB_STATE_UNAUTHENTICATED))
385: return -ENODEV;
386:
387: /* For now, get the endpoint from the pipe. Eventually drivers
388: * will be required to set urb->ep directly and we will eliminate
389: * urb->pipe.
390: */
391: ep = usb_pipe_endpoint(dev, urb->pipe);
392: if (!ep)
393: return -ENOENT;
394:
395: urb->ep = ep;
396: urb->status = -EINPROGRESS;
397: urb->actual_length = 0;
398:
399: /* Lots of sanity checks, so HCDs can rely on clean data
400: * and don't need to duplicate tests
401: */
402: xfertype = usb_endpoint_type(&ep->desc);
403: if (xfertype == USB_ENDPOINT_XFER_CONTROL) {
404: struct usb_ctrlrequest *setup =
405: (struct usb_ctrlrequest *) urb->setup_packet;
406:
407: if (!setup)
408: return -ENOEXEC;
409: is_out = !(setup->bRequestType & USB_DIR_IN) ||
410: !setup->wLength;
411: dev_WARN_ONCE(&dev->dev, (usb_pipeout(urb->pipe) != is_out),
412: "BOGUS control dir, pipe %x doesn't match bRequestType %x\n",
413: urb->pipe, setup->bRequestType);
414: if (le16_to_cpu(setup->wLength) != urb->transfer_buffer_length) {
415: dev_dbg(&dev->dev, "BOGUS control len %d doesn't match transfer length %d\n",
416: le16_to_cpu(setup->wLength),
417: urb->transfer_buffer_length);
418: return -EBADR;
419: }
420: } else {
421: is_out = usb_endpoint_dir_out(&ep->desc);
422: }
423:
424: /* Clear the internal flags and cache the direction for later use */
425: urb->transfer_flags &= ~(URB_DIR_MASK | URB_DMA_MAP_SINGLE |
426: URB_DMA_MAP_PAGE | URB_DMA_MAP_SG | URB_MAP_LOCAL |
427: URB_SETUP_MAP_SINGLE | URB_SETUP_MAP_LOCAL |
428: URB_DMA_SG_COMBINED);
429: urb->transfer_flags |= (is_out ? URB_DIR_OUT : URB_DIR_IN);
430: kmsan_handle_urb(urb, is_out);
431:
432: if (xfertype != USB_ENDPOINT_XFER_CONTROL &&
433: dev->state < USB_STATE_CONFIGURED)
434: return -ENODEV;
435:
436: max = usb_endpoint_maxp(&ep->desc);
437: is_eusb2_isoch_double = usb_endpoint_is_hs_isoc_double(dev, ep);
438: if (!max && !is_eusb2_isoch_double) {
439: dev_dbg(&dev->dev,
440: "bogus endpoint ep%d%s in %s (bad maxpacket %d)\n",
441: usb_endpoint_num(&ep->desc), is_out ? "out" : "in",
442: __func__, max);
443: return -EMSGSIZE;
444: }
445:
446: /* periodic transfers limit size per frame/uframe,
447: * but drivers only control those sizes for ISO.
448: * while we're checking, initialize return status.
449: */
450: if (xfertype == USB_ENDPOINT_XFER_ISOC) {
451: int n, len;
452:
453: /* SuperSpeed isoc endpoints have up to 16 bursts of up to
454: * 3 packets each
455: */
456: if (dev->speed >= USB_SPEED_SUPER) {
457: int burst = 1 + ep->ss_ep_comp.bMaxBurst;
458: int mult = USB_SS_MULT(ep->ss_ep_comp.bmAttributes);
459: max *= burst;
460: max *= mult;
461: }
462:
463: if (dev->speed == USB_SPEED_SUPER_PLUS &&
464: USB_SS_SSP_ISOC_COMP(ep->ss_ep_comp.bmAttributes)) {
465: struct usb_ssp_isoc_ep_comp_descriptor *isoc_ep_comp;
466:
467: isoc_ep_comp = &ep->ssp_isoc_ep_comp;
468: max = le32_to_cpu(isoc_ep_comp->dwBytesPerInterval);
469: }
470:
471: /* High speed, 1-3 packets/uframe, max 6 for eUSB2 double bw */
472: if (dev->speed == USB_SPEED_HIGH) {
473: if (is_eusb2_isoch_double)
474: max = le32_to_cpu(ep->eusb2_isoc_ep_comp.dwBytesPerInterval);
475: else
476: max *= usb_endpoint_maxp_mult(&ep->desc);
477: }
478:
479: if (urb->number_of_packets <= 0)
480: return -EINVAL;
481: for (n = 0; n < urb->number_of_packets; n++) {
482: len = urb->iso_frame_desc[n].length;
483: if (len < 0 || len > max)
484: return -EMSGSIZE;
485: urb->iso_frame_desc[n].status = -EXDEV;
486: urb->iso_frame_desc[n].actual_length = 0;
487: }
488: } else if (urb->num_sgs && !urb->dev->bus->no_sg_constraint) {
489: struct scatterlist *sg;
490: int i;
491:
492: for_each_sg(urb->sg, sg, urb->num_sgs - 1, i)
493: if (sg->length % max)
494: return -EINVAL;
495: }
496:
497: /* the I/O buffer must be mapped/unmapped, except when length=0 */
498: if (urb->transfer_buffer_length > INT_MAX)
499: return -EMSGSIZE;
500:
501: /*
502: * stuff that drivers shouldn't do, but which shouldn't
503: * cause problems in HCDs if they get it wrong.
504: */
505:
506: /* Check that the pipe's type matches the endpoint's type */
507: if (usb_pipe_type_check(urb->dev, urb->pipe))
508: dev_warn_once(&dev->dev, "BOGUS urb xfer, pipe %x != type %x\n",
509: usb_pipetype(urb->pipe), pipetypes[xfertype]);
510:
511: /* Check against a simple/standard policy */
512: allowed = (URB_NO_TRANSFER_DMA_MAP | URB_NO_INTERRUPT | URB_DIR_MASK |
513: URB_FREE_BUFFER);
514: switch (xfertype) {
515: case USB_ENDPOINT_XFER_BULK:
516: case USB_ENDPOINT_XFER_INT:
517: if (is_out)
518: allowed |= URB_ZERO_PACKET;
519: fallthrough;
520: default: /* all non-iso endpoints */
521: if (!is_out)
522: allowed |= URB_SHORT_NOT_OK;
523: break;
524: case USB_ENDPOINT_XFER_ISOC:
525: allowed |= URB_ISO_ASAP;
526: break;
527: }
528: allowed &= urb->transfer_flags;
529:
530: /* warn if submitter gave bogus flags */
531: if (allowed != urb->transfer_flags)
532: dev_WARN(&dev->dev, "BOGUS urb flags, %x --> %x\n",
533: urb->transfer_flags, allowed);
534:
535: /*
536: * Force periodic transfer intervals to be legal values that are
537: * a power of two (so HCDs don't need to).
538: *
539: * FIXME want bus->{intr,iso}_sched_horizon values here. Each HC
540: * supports different values... this uses EHCI/UHCI defaults (and
541: * EHCI can use smaller non-default values).
542: */
543: switch (xfertype) {
544: case USB_ENDPOINT_XFER_ISOC:
545: case USB_ENDPOINT_XFER_INT:
546: /* too small? */
547: if (urb->interval <= 0)
548: return -EINVAL;
549:
550: /* too big? */
551: switch (dev->speed) {
552: case USB_SPEED_SUPER_PLUS:
553: case USB_SPEED_SUPER: /* units are 125us */
554: /* Handle up to 2^(16-1) microframes */
555: if (urb->interval > (1 << 15))
556: return -EINVAL;
557: max = 1 << 15;
558: break;
559: case USB_SPEED_HIGH: /* units are microframes */
560: /* NOTE usb handles 2^15 */
561: if (urb->interval > (1024 * 8))
562: urb->interval = 1024 * 8;
563: max = 1024 * 8;
564: break;
565: case USB_SPEED_FULL: /* units are frames/msec */
566: case USB_SPEED_LOW:
567: if (xfertype == USB_ENDPOINT_XFER_INT) {
568: if (urb->interval > 255)
569: return -EINVAL;
570: /* NOTE ohci only handles up to 32 */
571: max = 128;
572: } else {
573: if (urb->interval > 1024)
574: urb->interval = 1024;
575: /* NOTE usb and ohci handle up to 2^15 */
576: max = 1024;
577: }
578: break;
579: default:
580: return -EINVAL;
581: }
582: /* Round down to a power of 2, no more than max */
583: urb->interval = min(max, 1 << ilog2(urb->interval));
584: }
585:
586: return usb_hcd_submit_urb(urb, mem_flags);
587: }
SourceFile:drivers/usb/core/urb.c]
|
| 40/2 |
2026/05/16 14:10 |
llm |
debugger |
0m
Model:
Tokens:
input: 52959
output: 47
thoughts: 869
Thoughts: **Analyzing USB URB Submission and Potential Double Adds**
Okay, let's break this down. First, `usb_submit_urb` checks if `urb->hcpriv` is set. The question is, if it's NULL but the URB is on `bh->head`, what happens? `usb_hcd_giveback_urb` puts the URB on `bh->head` and schedules a workqueue. This workqueue calls `__usb_hcd_giveback_urb`, which *does* set `urb->hcpriv = NULL` and calls `urb->complete`. So, crucial point: `urb->hcpriv` is NOT NULL *while* the URB is on `bh->head`. It becomes NULL inside `__usb_hcd_giveback_urb` right before `urb->complete`. Therefore, `usb_submit_urb` *will* return `-EBUSY` if the URB is indeed on `bh->head`.
Now, consider this: what if `cm109_submit_ctl` gets called from `cm109_urb_irq_callback`? Inside `cm109_urb_irq_callback`, `urb` is `dev->urb_irq`. It calls `cm109_submit_ctl(dev)`, which then calls `usb_submit_urb(dev->urb_ctl, GFP_ATOMIC)`. If `dev->urb_ctl` is already pending, `usb_submit_urb` will return `-EBUSY`. But, and this is important, `cm109_submit_ctl` sets `dev->ctl_urb_pending = 1`. If `usb_submit_urb` returns `-EBUSY`, `dev->ctl_urb_pending` *stays* at 1. So, it's a bug, but it doesn't cause a double-add issue.
Okay, let's drill down deeper. How *could* `usb_submit_urb` lead to a double-add? Examining `usb_hcd_submit_urb`:
```c
if (is_root_hub(urb->dev)) {
status = rh_urb_enqueue(hcd, urb);
} else {
status = map_urb_for_dma(hcd, urb, mem_flags);
if (likely(status == 0)) {
status = hcd->driver->urb_enqueue(hcd, urb, mem_flags);
if (unlikely(status))
unmap_urb_for_dma(hcd, urb);
}
}
```
If `urb_enqueue` (e.g., `dummy_urb_enqueue`) is called, it calls `usb_hcd_link_urb_to_ep`. `usb_hcd_link_urb_to_ep` does `list_add_tail(&urb->urb_list, &urb->ep->urb_list);`. *That's* where a double add could happen. It's the `urb->urb_list` potentially being added twice to a list. But, `usb_hcd_submit_urb` does `INIT_LIST_HEAD(&urb->urb_list)` *only* if `status` is non-zero. That is, the `INIT_LIST_HEAD` is *not* called if the urb is successfully submitted.
Crucially, `usb_alloc_urb` initializes `urb->urb_list`. So, we need to know what happens when an URB completes. Does it remove `urb->urb_list` from `urb->ep->urb_list`? Yes, `usb_hcd_unlink_urb_from_ep` *does* `list_del_init(&urb->urb_list);`. Okay, so that should cleanup the urb's link. Now, the problem lies in `usb_hcd_giveback_urb` which puts the URB on `bh->head` using `list_add_tail(&urb->urb_list, &bh->head);`! Then, `__usb_hcd_giveback_urb` is called from the workqueue. The question is: *Does* `__usb_hcd_giveback_urb` remove `urb->urb_list` from `bh->head`? Let's check `usb_giveback_urb_bh`. It seems like we may have found the issue and will cause a crash, and is where we need to focus next.
|
| 41/2 |
2026/05/16 14:10 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/core/hcd.c IncludeLines:true Name:usb_giveback_urb_bh]
Results: map[SourceCode:1674: static void usb_giveback_urb_bh(struct work_struct *work)
1675: {
1676: struct giveback_urb_bh *bh =
1677: container_of(work, struct giveback_urb_bh, bh);
1678: struct list_head local_list;
1679:
1680: spin_lock_irq(&bh->lock);
1681: bh->running = true;
1682: list_replace_init(&bh->head, &local_list);
1683: spin_unlock_irq(&bh->lock);
1684:
1685: while (!list_empty(&local_list)) {
1686: struct urb *urb;
1687:
1688: urb = list_entry(local_list.next, struct urb, urb_list);
1689: list_del_init(&urb->urb_list);
1690: bh->completing_ep = urb->ep;
1691: __usb_hcd_giveback_urb(urb);
1692: bh->completing_ep = NULL;
1693: }
1694:
1695: /*
1696: * giveback new URBs next time to prevent this function
1697: * from not exiting for a long time.
1698: */
1699: spin_lock_irq(&bh->lock);
1700: if (!list_empty(&bh->head)) {
1701: if (bh->high_prio)
1702: queue_work(system_bh_highpri_wq, &bh->bh);
1703: else
1704: queue_work(system_bh_wq, &bh->bh);
1705: }
1706: bh->running = false;
1707: spin_unlock_irq(&bh->lock);
1708: }
SourceFile:drivers/usb/core/hcd.c]
|
| 42/2 |
2026/05/16 14:10 |
llm |
debugger |
0m
Model:
Tokens:
input: 55427
output: 49
thoughts: 421
Thoughts: **Analyzing USB URB Resubmission Concurrency**
Okay, so `usb_giveback_urb_bh` cleverly removes the URB from the list *before* calling `__usb_hcd_giveback_urb(urb)`. That means when the completion routine (`urb->complete`) gets called, `urb->urb_list` is definitely initialized. This implies that if the completion routine resubmits the URB, the `list_add_tail` in `usb_hcd_link_urb_to_ep` should succeed because the list entry is guaranteed to be empty. No issues here.
Now, let's consider concurrency. What if `cm109_submit_buzz_toggle` gets called at the same time as `usb_submit_urb`? `cm109_toggle_buzzer_async` is the culprit, called by `cm109_input_ev`, which in turn comes from `input_inject_event`. My initial thought is to check if there are any locks.
Let's think this through... `cm109_toggle_buzzer_async` takes `dev->ctl_submit_lock`. Also, `cm109_submit_ctl` and `cm109_urb_ctl_callback` *also* take `dev->ctl_submit_lock`. So, those are all serialized! But, hold on a second... `cm109_submit_buzz_toggle` itself *doesn't* take that lock!
But... isn't `cm109_submit_buzz_toggle` only called from `cm109_toggle_buzzer_async` and `cm109_urb_ctl_callback`, which *do* hold the lock? We should ensure there are no other callers... let's check. Yes, good, there are not.
Okay, let's make sure we are not missing anything else. Could `cm109_input_open` possibly be calling `usb_submit_urb(dev->urb_ctl, GFP_KERNEL)` concurrently? I had better go look at `cm109_input_open` to verify.
|
| 43/2 |
2026/05/16 14:10 |
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]
|
| 44/2 |
2026/05/16 14:10 |
llm |
debugger |
0m
Model:
Tokens:
input: 57019
output: 23
thoughts: 3747
Thoughts: **Analysis of cm109 Driver Concurrency Issues**
Okay, so I'm diving into this `cm109` driver code and immediately see a potential race condition involving concurrent calls to `usb_submit_urb`. The function `cm109_input_open` doesn't take the `dev->ctl_submit_lock` before submitting `dev->urb_ctl`, which means it could be happening at the same time as `cm109_input_ev`, which *can* call the potentially problematic `cm109_toggle_buzzer_async`. This is triggered by an event injection and, from what I can tell, `input_inject_event` is called from `vt_ioctl` which could be happening at the same time.
`cm109_toggle_buzzer_async` *does* take the lock, and checks `dev->ctl_urb_pending`. If `cm109_input_open` has already set the flag, `cm109_toggle_buzzer_async` just sets `dev->buzzer_pending`. Otherwise, it *also* sets `dev->ctl_urb_pending = 1` and calls `cm109_submit_buzz_toggle`, which *also* calls `usb_submit_urb`. This could result in *two* concurrent `usb_submit_urb` calls!
And here's where things get interesting, `usb_submit_urb` has a check on `urb->hcpriv` which *should* prevent a double submission, but that check *isn't protected by a lock*! So if two threads call `usb_submit_urb` concurrently, they *both* might see `urb->hcpriv == NULL`, proceed to `usb_hcd_submit_urb`, which will then call `hcd->driver->urb_enqueue`. In our case, `dummy_urb_enqueue` gets called. `dummy_urb_enqueue` takes *its* own lock, and calls `usb_hcd_link_urb_to_ep`. `usb_hcd_link_urb_to_ep` *also* has a lock. Inside the lock it adds `urb->urb_list` to `urb->ep->urb_list`. If called concurrently, we can have a double add, which is a big deal!
Now, does `cm109_input_open` really have the only unlocked `usb_submit_urb`? Looking at `cm109_submit_ctl` (called from `cm109_urb_irq_callback`), it sets `dev->ctl_urb_pending = 1` and calls `usb_submit_urb` WITHOUT checking if `dev->ctl_urb_pending` *was* already 1! This *also* causes a potential double submission!
If `dev->ctl_urb_pending` was already 1, then the URB is already pending, and a concurrent call to `usb_submit_urb` *should* return `-EBUSY` and emit a warning. However, if the URB is completing concurrently, the check for `urb->hcpriv` might be zero, again, because the handler removes the urb from the active list.
Now, considering the crash and stack trace, it's clear the problem's in `cm109_toggle_buzzer_async` calling `usb_submit_urb`. This implies `dev->ctl_urb_pending` *was* 0 when the call happened which, based on the previous logic, should *not* happen.
`cm109_urb_ctl_callback` *does* set `dev->ctl_urb_pending = 0` after the URB completes and it's protected by a lock, so everything there seems ok. There are a few other places where it's zeroed out, but they shouldn't trigger this condition.
So how could `dev->ctl_urb_pending` be 0 while the URB *is* still on the list, causing a double-add? The key is that `cm109_urb_ctl_callback` clears `dev->ctl_urb_pending` after the URB is no longer on the list, so this can't be the issue.
What about `usb_submit_urb` failing? If `usb_submit_urb` fails in `cm109_submit_buzz_toggle`, `dev->ctl_urb_pending` would *remain* set to 1, preventing the erroneous code path. Which means that's not the issue.
Okay, let's go back to the question of *how* the crash is happening. I think I have it. In the call to
|
| 45/2 |
2026/05/16 14:10 |
tool |
codesearch-find-references |
0m
Args:
Results: map[References:[map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:__do_poll SourceFile:drivers/mmc/host/vub300.c SourceLine:668] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:__do_poll SourceFile:drivers/mmc/host/vub300.c SourceLine:669] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:__lf_x_usb_disable_rx SourceFile:drivers/net/wireless/purelifi/plfxlc/usb.c SourceLine:286] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:__lf_x_usb_enable_rx SourceFile:drivers/net/wireless/purelifi/plfxlc/usb.c SourceLine:240] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:__pegasus_open SourceFile:drivers/input/tablet/pegasus_notetaker.c SourceLine:229] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:__usbnet_status_stop_force SourceFile:drivers/net/usb/usbnet.c SourceLine:320] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:__vub300_command_response SourceFile:drivers/mmc/host/vub300.c SourceLine:1603] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:__vub300_command_response SourceFile:drivers/mmc/host/vub300.c SourceLine:1604] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:__vub300_command_response SourceFile:drivers/mmc/host/vub300.c SourceLine:1614] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:__vub300_command_response SourceFile:drivers/mmc/host/vub300.c SourceLine:1615] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:_chaoskey_fill SourceFile:drivers/usb/misc/chaoskey.c SourceLine:400] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:_chaoskey_fill SourceFile:drivers/usb/misc/chaoskey.c SourceLine:406] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:acm_port_activate SourceFile:drivers/usb/class/cdc-acm.c SourceLine:744] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:acm_port_activate SourceFile:drivers/usb/class/cdc-acm.c SourceLine:745] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:acm_softint SourceFile:drivers/usb/class/cdc-acm.c SourceLine:621] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:adu_abort_transfers SourceFile:drivers/usb/misc/adutux.c SourceLine:129] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:adu_abort_transfers SourceFile:drivers/usb/misc/adutux.c SourceLine:138] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:aiptek_close SourceFile:drivers/input/tablet/aiptek.c SourceLine:847] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:aiptek_disconnect SourceFile:drivers/input/tablet/aiptek.c SourceLine:1879] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:airspy_kill_urbs SourceFile:drivers/media/usb/airspy/airspy.c SourceLine:322] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:appledisplay_disconnect SourceFile:drivers/usb/misc/appledisplay.c SourceLine:331] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:appledisplay_probe SourceFile:drivers/usb/misc/appledisplay.c SourceLine:311] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:ar5523_cancel_rx_bufs SourceFile:drivers/net/wireless/ath/ar5523/ar5523.c SourceLine:693] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:ar5523_cancel_rx_cmd SourceFile:drivers/net/wireless/ath/ar5523/ar5523.c SourceLine:193] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:ar5523_cancel_tx_cmd SourceFile:drivers/net/wireless/ath/ar5523/ar5523.c SourceLine:246] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:ark3116_close SourceFile:drivers/usb/serial/ark3116.c SourceLine:308] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:as102_usb_stop_stream SourceFile:drivers/media/usb/as102/as102_usb_drv.c SourceLine:283] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:ath9k_hif_usb_dealloc_tx_urbs SourceFile:drivers/net/wireless/ath/ath9k/hif_usb.c SourceLine:828] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:ati_remote2_kill_urbs SourceFile:drivers/input/misc/ati_remote2.c SourceLine:232] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:ati_remote2_kill_urbs SourceFile:drivers/input/misc/ati_remote2.c SourceLine:233] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:ati_remote2_submit_urbs SourceFile:drivers/input/misc/ati_remote2.c SourceLine:221] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:ati_remote_close SourceFile:drivers/media/rc/ati_remote.c SourceLine:350] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:ati_remote_disconnect SourceFile:drivers/media/rc/ati_remote.c SourceLine:950] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:ati_remote_disconnect SourceFile:drivers/media/rc/ati_remote.c SourceLine:951] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:ati_remote_probe SourceFile:drivers/media/rc/ati_remote.c SourceLine:925] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:ati_remote_probe SourceFile:drivers/media/rc/ati_remote.c SourceLine:926] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:ati_remote_sendpacket SourceFile:drivers/media/rc/ati_remote.c SourceLine:425] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:atp_close SourceFile:drivers/input/mouse/appletouch.c SourceLine:809] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:atp_disconnect SourceFile:drivers/input/mouse/appletouch.c SourceLine:936] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:atp_suspend SourceFile:drivers/input/mouse/appletouch.c SourceLine:965] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:atusb_disconnect SourceFile:drivers/net/ieee802154/atusb.c SourceLine:1073] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:atusb_probe SourceFile:drivers/net/ieee802154/atusb.c SourceLine:1056] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:au0828_uninit_isoc SourceFile:drivers/media/usb/au0828/au0828-video.c SourceLine:166] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:au0828_v4l2_suspend SourceFile:drivers/media/usb/au0828/au0828-video.c SourceLine:1710] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:bcd2000_free_usb_related_resources SourceFile:sound/usb/bcd2000/bcd2000.c SourceLine:351] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:bcd2000_free_usb_related_resources SourceFile:sound/usb/bcd2000/bcd2000.c SourceLine:352] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:bcd2000_midi_output_close SourceFile:sound/usb/bcd2000/bcd2000.c SourceLine:182] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:bcm203x_disconnect SourceFile:drivers/bluetooth/bcm203x.c SourceLine:242] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:bcm5974_pause_traffic SourceFile:drivers/input/mouse/bcm5974.c SourceLine:853] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:bcm5974_pause_traffic SourceFile:drivers/input/mouse/bcm5974.c SourceLine:854] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:bcm5974_start_traffic SourceFile:drivers/input/mouse/bcm5974.c SourceLine:844] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:belkin_sa_close SourceFile:drivers/usb/serial/belkin_sa.c SourceLine:166] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:belkin_sa_open SourceFile:drivers/usb/serial/belkin_sa.c SourceLine:158] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:bfusb_unlink_urbs SourceFile:drivers/bluetooth/bfusb.c SourceLine:103] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:catc_stop SourceFile:drivers/net/usb/catc.c SourceLine:746] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:catc_stop SourceFile:drivers/net/usb/catc.c SourceLine:747] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:catc_stop SourceFile:drivers/net/usb/catc.c SourceLine:748] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:catc_stop SourceFile:drivers/net/usb/catc.c SourceLine:749] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:ch341_close SourceFile:drivers/usb/serial/ch341.c SourceLine:446] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:ch341_open SourceFile:drivers/usb/serial/ch341.c SourceLine:480] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:cleanup_urbs SourceFile:sound/usb/line6/pcm.c SourceLine:459] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:cm109_stop_traffic SourceFile:drivers/input/misc/cm109.c SourceLine:518] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:cm109_stop_traffic SourceFile:drivers/input/misc/cm109.c SourceLine:519] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:cx231xx_bulk_audio_deinit SourceFile:drivers/media/usb/cx231xx/cx231xx-audio.c SourceLine:64] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:cx231xx_isoc_audio_deinit SourceFile:drivers/media/usb/cx231xx/cx231xx-audio.c SourceLine:40] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:cx231xx_uninit_bulk SourceFile:drivers/media/usb/cx231xx/cx231xx-core.c SourceLine:937] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:cx231xx_uninit_isoc SourceFile:drivers/media/usb/cx231xx/cx231xx-core.c SourceLine:878] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:cx231xx_uninit_vbi_isoc SourceFile:drivers/media/usb/cx231xx/cx231xx-vbi.c SourceLine:307] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:cxacru_unbind SourceFile:drivers/usb/atm/cxacru.c SourceLine:1258] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:cxacru_unbind SourceFile:drivers/usb/atm/cxacru.c SourceLine:1259] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:cxusb_medion_v_stop_streaming SourceFile:drivers/media/usb/dvb-usb/cxusb-analog.c SourceLine:924] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:cyberjack_close SourceFile:drivers/usb/serial/cyberjack.c SourceLine:153] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:cyberjack_close SourceFile:drivers/usb/serial/cyberjack.c SourceLine:154] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:cyberjack_port_remove SourceFile:drivers/usb/serial/cyberjack.c SourceLine:126] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:cypress_close SourceFile:drivers/usb/serial/cypress_m8.c SourceLine:645] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:cypress_close SourceFile:drivers/usb/serial/cypress_m8.c SourceLine:646] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:destroy_async SourceFile:drivers/usb/core/devio.c SourceLine:678] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:destroy_urbs SourceFile:drivers/media/usb/gspca/gspca.c SourceLine:478] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:digi_close SourceFile:drivers/usb/serial/digi_acceleport.c SourceLine:1161] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:digi_disconnect SourceFile:drivers/usb/serial/digi_acceleport.c SourceLine:1261] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:digi_disconnect SourceFile:drivers/usb/serial/digi_acceleport.c SourceLine:1262] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:dln2_stop_rx_urbs SourceFile:drivers/mfd/dln2.c SourceLine:595] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:edge_close SourceFile:drivers/usb/serial/io_edgeport.c SourceLine:1095] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:edge_close SourceFile:drivers/usb/serial/io_ti.c SourceLine:1979] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:edge_close SourceFile:drivers/usb/serial/io_ti.c SourceLine:1980] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:edge_close SourceFile:drivers/usb/serial/io_ti.c SourceLine:1993] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:edge_disconnect SourceFile:drivers/usb/serial/io_edgeport.c SourceLine:2931] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:edge_disconnect SourceFile:drivers/usb/serial/io_edgeport.c SourceLine:2932] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:edge_open SourceFile:drivers/usb/serial/io_ti.c SourceLine:1956] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:edge_release SourceFile:drivers/usb/serial/io_edgeport.c SourceLine:2946] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:edge_release SourceFile:drivers/usb/serial/io_edgeport.c SourceLine:2950] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:em28xx_deinit_isoc_audio SourceFile:drivers/media/usb/em28xx/em28xx-audio.c SourceLine:58] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:em28xx_stop_urbs SourceFile:drivers/media/usb/em28xx/em28xx-core.c SourceLine:995] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:em28xx_uninit_usb_xfer SourceFile:drivers/media/usb/em28xx/em28xx-core.c SourceLine:960] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:es2_arpc_in_disable SourceFile:drivers/greybus/es2.c SourceLine:304] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:es2_arpc_in_enable SourceFile:drivers/greybus/es2.c SourceLine:291] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:es2_cport_in_disable SourceFile:drivers/greybus/es2.c SourceLine:265] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:es2_cport_in_enable SourceFile:drivers/greybus/es2.c SourceLine:251] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:es2_destroy SourceFile:drivers/greybus/es2.c SourceLine:784] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:f81232_close SourceFile:drivers/usb/serial/f81232.c SourceLine:751] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:f81232_open SourceFile:drivers/usb/serial/f81232.c SourceLine:720] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:f81232_suspend SourceFile:drivers/usb/serial/f81232.c SourceLine:956] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:f81232_suspend SourceFile:drivers/usb/serial/f81232.c SourceLine:958] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:f81534_close SourceFile:drivers/usb/serial/f81534.c SourceLine:1115] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:f81534_close SourceFile:drivers/usb/serial/f81534.c SourceLine:1127] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:fcp_cleanup_urb SourceFile:sound/usb/fcp.c SourceLine:899] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:firm_send_command SourceFile:drivers/usb/serial/whiteheat.c SourceLine:564] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:flexcop_usb_transfer_exit SourceFile:drivers/media/usb/b2c2/flexcop-usb.c SourceLine:415] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:free_urbs SourceFile:sound/usb/caiaq/audio.c SourceLine:739] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:garmin_close SourceFile:drivers/usb/serial/garmin_gps.c SourceLine:940] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:garmin_init_session SourceFile:drivers/usb/serial/garmin_gps.c SourceLine:872] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:garmin_init_session SourceFile:drivers/usb/serial/garmin_gps.c SourceLine:899] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:garmin_open SourceFile:drivers/usb/serial/garmin_gps.c SourceLine:919] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:garmin_port_remove SourceFile:drivers/usb/serial/garmin_gps.c SourceLine:1407] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:gnss_usb_close SourceFile:drivers/gnss/usb.c SourceLine:89] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:go7007_usb_release SourceFile:drivers/media/usb/go7007/go7007-usb.c SourceLine:903] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:go7007_usb_release SourceFile:drivers/media/usb/go7007/go7007-usb.c SourceLine:912] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:go7007_usb_release SourceFile:drivers/media/usb/go7007/go7007-usb.c SourceLine:918] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:go7007_usb_stream_start SourceFile:drivers/media/usb/go7007/go7007-usb.c SourceLine:857] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:go7007_usb_stream_start SourceFile:drivers/media/usb/go7007/go7007-usb.c SourceLine:860] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:go7007_usb_stream_stop SourceFile:drivers/media/usb/go7007/go7007-usb.c SourceLine:872] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:go7007_usb_stream_stop SourceFile:drivers/media/usb/go7007/go7007-usb.c SourceLine:875] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:gspca_input_destroy_urb SourceFile:drivers/media/usb/gspca/gspca.c SourceLine:237] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:hackrf_kill_urbs SourceFile:drivers/media/usb/hackrf/hackrf.c SourceLine:578] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:hanwang_close SourceFile:drivers/input/tablet/hanwang.c SourceLine:294] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:hdpvr_cancel_queue SourceFile:drivers/media/usb/hdpvr/hdpvr-video.c SourceLine:93] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:hid_cease_io SourceFile:drivers/hid/usbhid/hid-core.c SourceLine:1488] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:hid_cease_io SourceFile:drivers/hid/usbhid/hid-core.c SourceLine:1489] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:hid_cease_io SourceFile:drivers/hid/usbhid/hid-core.c SourceLine:1490] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:hif_usb_stop SourceFile:drivers/net/wireless/ath/ath9k/hif_usb.c SourceLine:454] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:hiface_pcm_stream_stop SourceFile:sound/usb/hiface/pcm.c SourceLine:196] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:hso_stop_net_device SourceFile:drivers/net/usb/hso.c SourceLine:2122] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:hso_stop_net_device SourceFile:drivers/net/usb/hso.c SourceLine:2126] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:hso_stop_serial_device SourceFile:drivers/net/usb/hso.c SourceLine:2189] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:hso_stop_serial_device SourceFile:drivers/net/usb/hso.c SourceLine:2196] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:hso_stop_serial_device SourceFile:drivers/net/usb/hso.c SourceLine:2206] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:hso_stop_serial_device SourceFile:drivers/net/usb/hso.c SourceLine:2213] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:hub_quiesce SourceFile:drivers/usb/core/hub.c SourceLine:1412] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:iforce_usb_stop_io SourceFile:drivers/input/joystick/iforce/iforce-usb.c SourceLine:126] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:iforce_usb_stop_io SourceFile:drivers/input/joystick/iforce/iforce-usb.c SourceLine:127] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:iguanair_disconnect SourceFile:drivers/media/rc/iguanair.c SourceLine:501] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:iguanair_disconnect SourceFile:drivers/media/rc/iguanair.c SourceLine:502] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:iguanair_probe SourceFile:drivers/media/rc/iguanair.c SourceLine:480] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:iguanair_probe SourceFile:drivers/media/rc/iguanair.c SourceLine:481] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:iguanair_send SourceFile:drivers/media/rc/iguanair.c SourceLine:198] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:iguanair_suspend SourceFile:drivers/media/rc/iguanair.c SourceLine:522] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:iguanair_suspend SourceFile:drivers/media/rc/iguanair.c SourceLine:523] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:imon_disconnect SourceFile:drivers/media/rc/imon.c SourceLine:2533] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:imon_disconnect SourceFile:drivers/media/rc/imon.c SourceLine:2540] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:imon_disconnect SourceFile:drivers/media/rc/imon.c SourceLine:2551] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:imon_disconnect SourceFile:drivers/media/rc/imon_raw.c SourceLine:194] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:imon_init_intf0 SourceFile:drivers/media/rc/imon.c SourceLine:2308] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:imon_suspend SourceFile:drivers/media/rc/imon.c SourceLine:2571] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:imon_suspend SourceFile:drivers/media/rc/imon.c SourceLine:2573] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:ims_pcu_buffers_free SourceFile:drivers/input/misc/ims-pcu.c SourceLine:1624] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:ims_pcu_buffers_free SourceFile:drivers/input/misc/ims-pcu.c SourceLine:1632] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:ims_pcu_start_io SourceFile:drivers/input/misc/ims-pcu.c SourceLine:1770] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:ims_pcu_stop_io SourceFile:drivers/input/misc/ims-pcu.c SourceLine:1779] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:ims_pcu_stop_io SourceFile:drivers/input/misc/ims-pcu.c SourceLine:1780] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:init_card SourceFile:sound/usb/caiaq/device.c SourceLine:522] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:input_close SourceFile:drivers/input/misc/yealink.c SourceLine:542] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:input_close SourceFile:drivers/input/misc/yealink.c SourceLine:543] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:iowarrior_disconnect SourceFile:drivers/usb/misc/iowarrior.c SourceLine:913] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:iowarrior_release SourceFile:drivers/usb/misc/iowarrior.c SourceLine:675] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:ipheth_kill_urbs SourceFile:drivers/net/usb/ipheth.c SourceLine:178] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:ipheth_kill_urbs SourceFile:drivers/net/usb/ipheth.c SourceLine:179] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:irtoy_command SourceFile:drivers/media/rc/ir_toy.c SourceLine:255] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:irtoy_disconnect SourceFile:drivers/media/rc/ir_toy.c SourceLine:535] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:irtoy_disconnect SourceFile:drivers/media/rc/ir_toy.c SourceLine:537] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:irtoy_probe SourceFile:drivers/media/rc/ir_toy.c SourceLine:517] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:irtoy_probe SourceFile:drivers/media/rc/ir_toy.c SourceLine:519] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:iuu_close SourceFile:drivers/usb/serial/iuu_phoenix.c SourceLine:942] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:iuu_close SourceFile:drivers/usb/serial/iuu_phoenix.c SourceLine:943] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:kaweth_kill_urbs SourceFile:drivers/net/usb/kaweth.c SourceLine:632] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:kaweth_kill_urbs SourceFile:drivers/net/usb/kaweth.c SourceLine:633] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:kaweth_kill_urbs SourceFile:drivers/net/usb/kaweth.c SourceLine:634] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:kaweth_kill_urbs SourceFile:drivers/net/usb/kaweth.c SourceLine:640] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:kaweth_kill_urbs SourceFile:drivers/net/usb/kaweth.c SourceLine:641] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:kaweth_open SourceFile:drivers/net/usb/kaweth.c SourceLine:612] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:kbtab_close SourceFile:drivers/input/tablet/kbtab.c SourceLine:106] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:keyspan_close SourceFile:drivers/input/misc/keyspan_remote.c SourceLine:420] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:keyspan_close SourceFile:drivers/usb/serial/keyspan.c SourceLine:1595] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:keyspan_close SourceFile:drivers/usb/serial/keyspan.c SourceLine:1597] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:keyspan_close SourceFile:drivers/usb/serial/keyspan.c SourceLine:1598] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:keyspan_disconnect SourceFile:drivers/input/misc/keyspan_remote.c SourceLine:549] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:keyspan_disconnect SourceFile:drivers/usb/serial/keyspan.c SourceLine:2848] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:keyspan_disconnect SourceFile:drivers/usb/serial/keyspan.c SourceLine:2849] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:keyspan_disconnect SourceFile:drivers/usb/serial/keyspan.c SourceLine:2850] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:keyspan_pda_close SourceFile:drivers/usb/serial/keyspan_pda.c SourceLine:601] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:keyspan_pda_close SourceFile:drivers/usb/serial/keyspan_pda.c SourceLine:602] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:keyspan_pda_rx_throttle SourceFile:drivers/usb/serial/keyspan_pda.c SourceLine:233] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:keyspan_port_remove SourceFile:drivers/usb/serial/keyspan.c SourceLine:2977] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:keyspan_port_remove SourceFile:drivers/usb/serial/keyspan.c SourceLine:2978] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:keyspan_port_remove SourceFile:drivers/usb/serial/keyspan.c SourceLine:2980] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:keyspan_port_remove SourceFile:drivers/usb/serial/keyspan.c SourceLine:2981] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:keyspan_release SourceFile:drivers/usb/serial/keyspan.c SourceLine:2860] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:keyspan_release SourceFile:drivers/usb/serial/keyspan.c SourceLine:2861] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:kill_midi_urbs SourceFile:sound/usb/midi2.c SourceLine:235] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:kill_stream_urbs SourceFile:sound/usb/misc/ua101.c SourceLine:446] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:kobil_close SourceFile:drivers/usb/serial/kobil_sct.c SourceLine:238] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:kobil_close SourceFile:drivers/usb/serial/kobil_sct.c SourceLine:239] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:kobil_write SourceFile:drivers/usb/serial/kobil_sct.c SourceLine:302] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:lan78xx_disconnect SourceFile:drivers/net/usb/lan78xx.c SourceLine:4568] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:lan78xx_stop SourceFile:drivers/net/usb/lan78xx.c SourceLine:3526] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:lan78xx_suspend SourceFile:drivers/net/usb/lan78xx.c SourceLine:5128] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:ld_usb_abort_transfers SourceFile:drivers/usb/misc/ldusb.c SourceLine:195] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:ld_usb_abort_transfers SourceFile:drivers/usb/misc/ldusb.c SourceLine:198] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:line6_stop_listen SourceFile:sound/usb/line6/driver.c SourceLine:94] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:ljca_disconnect SourceFile:drivers/usb/misc/usb-ljca.c SourceLine:852] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:ljca_enumerate_clients SourceFile:drivers/usb/misc/usb-ljca.c SourceLine:741] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:ljca_suspend SourceFile:drivers/usb/misc/usb-ljca.c SourceLine:871] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:lme2510_exit SourceFile:drivers/media/usb/dvb-usb-v2/lmedm04.c SourceLine:1198] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:mce_write SourceFile:drivers/media/rc/mceusb.c SourceLine:858] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:mceusb_dev_disconnect SourceFile:drivers/media/rc/mceusb.c SourceLine:1848] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:mceusb_dev_probe SourceFile:drivers/media/rc/mceusb.c SourceLine:1820] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:mceusb_dev_suspend SourceFile:drivers/media/rc/mceusb.c SourceLine:1860] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:mct_u232_close SourceFile:drivers/usb/serial/mct_u232.c SourceLine:499] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:mct_u232_close SourceFile:drivers/usb/serial/mct_u232.c SourceLine:500] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:mct_u232_open SourceFile:drivers/usb/serial/mct_u232.c SourceLine:467] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:mdc800_device_read SourceFile:drivers/usb/image/mdc800.c SourceLine:737] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:mdc800_device_release SourceFile:drivers/usb/image/mdc800.c SourceLine:662] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:mdc800_device_release SourceFile:drivers/usb/image/mdc800.c SourceLine:663] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:mdc800_device_release SourceFile:drivers/usb/image/mdc800.c SourceLine:664] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:mdc800_device_write SourceFile:drivers/usb/image/mdc800.c SourceLine:865] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:mdc800_usb_disconnect SourceFile:drivers/usb/image/mdc800.c SourceLine:556] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:mdc800_usb_disconnect SourceFile:drivers/usb/image/mdc800.c SourceLine:557] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:mdc800_usb_disconnect SourceFile:drivers/usb/image/mdc800.c SourceLine:558] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:message_cancel SourceFile:drivers/greybus/es2.c SourceLine:477] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:metrousb_cleanup SourceFile:drivers/usb/serial/metro-usb.c SourceLine:165] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:metrousb_open SourceFile:drivers/usb/serial/metro-usb.c SourceLine:213] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:mos7720_close SourceFile:drivers/usb/serial/mos7720.c SourceLine:978] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:mos7720_close SourceFile:drivers/usb/serial/mos7720.c SourceLine:990] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:mos7720_close SourceFile:drivers/usb/serial/mos7720.c SourceLine:991] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:mos7720_release SourceFile:drivers/usb/serial/mos7720.c SourceLine:1665] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:mos7840_close SourceFile:drivers/usb/serial/mos7840.c SourceLine:753] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:mos7840_close SourceFile:drivers/usb/serial/mos7840.c SourceLine:763] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:mos7840_port_remove SourceFile:drivers/usb/serial/mos7840.c SourceLine:1718] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:mos7840_suspend SourceFile:drivers/usb/serial/mos7840.c SourceLine:1739] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:msi2500_iso_stop SourceFile:drivers/media/usb/msi2500/msi2500.c SourceLine:442] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:mts_urb_abort SourceFile:drivers/usb/image/microtek.c SourceLine:307] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:mts_usb_disconnect SourceFile:drivers/usb/image/microtek.c SourceLine:756] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:navman_close SourceFile:drivers/usb/serial/navman.c SourceLine:84] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:nexio_exit SourceFile:drivers/input/touchscreen/usbtouchscreen.c SourceLine:1045] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:onetouch_release_input SourceFile:drivers/usb/storage/onetouch.c SourceLine:265] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:oti6858_close SourceFile:drivers/usb/serial/oti6858.c SourceLine:573] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:oti6858_close SourceFile:drivers/usb/serial/oti6858.c SourceLine:574] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:oti6858_close SourceFile:drivers/usb/serial/oti6858.c SourceLine:575] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:pegasus_close SourceFile:drivers/input/tablet/pegasus_notetaker.c SourceLine:262] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:pegasus_open SourceFile:drivers/net/usb/pegasus.c SourceLine:880] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:pegasus_open SourceFile:drivers/net/usb/pegasus.c SourceLine:888] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:pegasus_open SourceFile:drivers/net/usb/pegasus.c SourceLine:889] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:pegasus_suspend SourceFile:drivers/input/tablet/pegasus_notetaker.c SourceLine:430] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:pegasus_suspend SourceFile:drivers/net/usb/pegasus.c SourceLine:1280] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:pegasus_suspend SourceFile:drivers/net/usb/pegasus.c SourceLine:1281] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:pl2303_close SourceFile:drivers/usb/serial/pl2303.c SourceLine:989] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:pl2303_open SourceFile:drivers/usb/serial/pl2303.c SourceLine:1027] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:pn533_usb_abort_cmd SourceFile:drivers/nfc/pn533/usb.c SourceLine:225] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:pn533_usb_disconnect SourceFile:drivers/nfc/pn533/usb.c SourceLine:609] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:pn533_usb_disconnect SourceFile:drivers/nfc/pn533/usb.c SourceLine:610] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:pn533_usb_disconnect SourceFile:drivers/nfc/pn533/usb.c SourceLine:611] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:pn533_usb_probe SourceFile:drivers/nfc/pn533/usb.c SourceLine:584] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:pn533_usb_probe SourceFile:drivers/nfc/pn533/usb.c SourceLine:585] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:pn533_usb_probe SourceFile:drivers/nfc/pn533/usb.c SourceLine:586] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:port100_abort_cmd SourceFile:drivers/nfc/port100.c SourceLine:963] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:port100_disconnect SourceFile:drivers/nfc/port100.c SourceLine:1618] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:port100_disconnect SourceFile:drivers/nfc/port100.c SourceLine:1619] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:port100_probe SourceFile:drivers/nfc/port100.c SourceLine:1600] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:port100_probe SourceFile:drivers/nfc/port100.c SourceLine:1602] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:port100_send_ack SourceFile:drivers/nfc/port100.c SourceLine:736] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:port100_send_frame_async SourceFile:drivers/nfc/port100.c SourceLine:789] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:powermate_disconnect SourceFile:drivers/input/misc/powermate.c SourceLine:419] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:powermate_disconnect SourceFile:drivers/input/misc/powermate.c SourceLine:421] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:powermate_probe SourceFile:drivers/input/misc/powermate.c SourceLine:402] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:powerz_disconnect SourceFile:drivers/hwmon/powerz.c SourceLine:258] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:powerz_read_data SourceFile:drivers/hwmon/powerz.c SourceLine:137] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:powerz_read_data SourceFile:drivers/hwmon/powerz.c SourceLine:142] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:proc_unlinkurb SourceFile:drivers/usb/core/devio.c SourceLine:2024] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:process_resetdev_request SourceFile:drivers/usb/serial/garmin_gps.c SourceLine:836] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:pvr2_buffer_wipe SourceFile:drivers/media/usb/pvrusb2/pvrusb2-io.c SourceLine:243] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:pvr2_hdw_remove_usb_stuff SourceFile:drivers/media/usb/pvrusb2/pvrusb2-hdw.c SourceLine:2631] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:pvr2_hdw_remove_usb_stuff SourceFile:drivers/media/usb/pvrusb2/pvrusb2-hdw.c SourceLine:2636] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:pwc_iso_stop SourceFile:drivers/media/usb/pwc/pwc-if.c SourceLine:514] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:pxrc_close SourceFile:drivers/input/joystick/pxrc.c SourceLine:106] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:pxrc_pre_reset SourceFile:drivers/input/joystick/pxrc.c SourceLine:231] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:pxrc_suspend SourceFile:drivers/input/joystick/pxrc.c SourceLine:210] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:qt2_close SourceFile:drivers/usb/serial/quatech2.c SourceLine:393] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:qt2_disconnect SourceFile:drivers/usb/serial/quatech2.c SourceLine:431] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:qt2_release SourceFile:drivers/usb/serial/quatech2.c SourceLine:140] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:redrat3_delete SourceFile:drivers/media/rc/redrat3.c SourceLine:455] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:redrat3_delete SourceFile:drivers/media/rc/redrat3.c SourceLine:456] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:redrat3_delete SourceFile:drivers/media/rc/redrat3.c SourceLine:457] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:redrat3_delete SourceFile:drivers/media/rc/redrat3.c SourceLine:458] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:redrat3_dev_suspend SourceFile:drivers/media/rc/redrat3.c SourceLine:1148] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:redrat3_dev_suspend SourceFile:drivers/media/rc/redrat3.c SourceLine:1149] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:redrat3_dev_suspend SourceFile:drivers/media/rc/redrat3.c SourceLine:1150] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:rtl2832_sdr_kill_urbs SourceFile:drivers/media/dvb-frontends/rtl2832_sdr.c SourceLine:274] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:rtl8150_open SourceFile:drivers/net/usb/rtl8150.c SourceLine:769] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:rtl8150_suspend SourceFile:drivers/net/usb/rtl8150.c SourceLine:534] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:rtl8150_suspend SourceFile:drivers/net/usb/rtl8150.c SourceLine:535] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:rtl8152_close SourceFile:drivers/net/usb/r8152.c SourceLine:7160] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:rtl8152_pre_reset SourceFile:drivers/net/usb/r8152.c SourceLine:8346] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:rtl8152_runtime_suspend SourceFile:drivers/net/usb/r8152.c SourceLine:8513] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:rtl8152_system_suspend SourceFile:drivers/net/usb/r8152.c SourceLine:8547] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:rtl_disable SourceFile:drivers/net/usb/r8152.c SourceLine:3747] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:rtl_stop_rx SourceFile:drivers/net/usb/r8152.c SourceLine:3550] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:s2255_destroy SourceFile:drivers/media/usb/s2255/s2255drv.c SourceLine:1490] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:s2255_stop_readpipe SourceFile:drivers/media/usb/s2255/s2255drv.c SourceLine:2173] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:send_packet SourceFile:drivers/media/rc/imon.c SourceLine:653] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:si470x_usb_driver_disconnect SourceFile:drivers/media/radio/si470x/radio-si470x-usb.c SourceLine:823] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:si470x_usb_driver_probe SourceFile:drivers/media/radio/si470x/radio-si470x-usb.c SourceLine:748] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:si470x_usb_driver_suspend SourceFile:drivers/media/radio/si470x/radio-si470x-usb.c SourceLine:782] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:sierra_stop_rx_urbs SourceFile:drivers/usb/serial/sierra.c SourceLine:656] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:sierra_stop_rx_urbs SourceFile:drivers/usb/serial/sierra.c SourceLine:658] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:simple_io SourceFile:drivers/usb/misc/usbtest.c SourceLine:482] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:sisusb_bulkin_msg SourceFile:drivers/usb/misc/sisusbvga/sisusbvga.c SourceLine:284] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:sisusb_bulkout_msg SourceFile:drivers/usb/misc/sisusbvga/sisusbvga.c SourceLine:233] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:sisusb_kill_all_busy SourceFile:drivers/usb/misc/sisusbvga/sisusbvga.c SourceLine:117] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:smsusb_stop_streaming SourceFile:drivers/media/usb/siano/smsusb.c SourceLine:181] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:snd_disconnect SourceFile:sound/usb/caiaq/device.c SourceLine:568] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:snd_disconnect SourceFile:sound/usb/caiaq/device.c SourceLine:569] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:snd_usb_caiaq_input_close SourceFile:sound/usb/caiaq/input.c SourceLine:562] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:snd_usb_caiaq_input_disconnect SourceFile:sound/usb/caiaq/input.c SourceLine:837] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:snd_usb_caiaq_midi_output_close SourceFile:sound/usb/caiaq/midi.c SourceLine:46] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:snd_usb_mixer_disconnect SourceFile:sound/usb/mixer.c SourceLine:3802] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:snd_usb_mixer_disconnect SourceFile:sound/usb/mixer.c SourceLine:3804] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:snd_usb_mixer_inactivate SourceFile:sound/usb/mixer.c SourceLine:3813] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:snd_usb_mixer_inactivate SourceFile:sound/usb/mixer.c SourceLine:3814] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:snd_usbmidi_disconnect SourceFile:sound/usb/midi.c SourceLine:1556] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:snd_usbmidi_disconnect SourceFile:sound/usb/midi.c SourceLine:1567] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:snd_usbmidi_input_stop SourceFile:sound/usb/midi.c SourceLine:2431] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:snd_usx2y_disconnect SourceFile:sound/usb/usx2y/usbusx2y.c SourceLine:422] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:speedtch_atm_stop SourceFile:drivers/usb/atm/speedtch.c SourceLine:702] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:speedtch_atm_stop SourceFile:drivers/usb/atm/speedtch.c SourceLine:709] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:stk1160_cancel_isoc SourceFile:drivers/media/usb/stk1160/stk1160-video.c SourceLine:353] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:stop_command_port SourceFile:drivers/usb/serial/whiteheat.c SourceLine:795] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:stop_urb_transfer SourceFile:drivers/media/usb/au0828/au0828-dvb.c SourceLine:177] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:stop_urbs SourceFile:drivers/usb/serial/usb_wwan.c SourceLine:538] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:stop_urbs SourceFile:drivers/usb/serial/usb_wwan.c SourceLine:540] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:stop_urbs SourceFile:drivers/usb/serial/usb_wwan.c SourceLine:541] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:stream_stop SourceFile:sound/usb/caiaq/audio.c SourceLine:127] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:stream_stop SourceFile:sound/usb/caiaq/audio.c SourceLine:130] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:streamzap_disconnect SourceFile:drivers/media/rc/streamzap.c SourceLine:401] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:streamzap_suspend SourceFile:drivers/media/rc/streamzap.c SourceLine:413] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:stub_device_cleanup_urbs SourceFile:drivers/usb/usbip/stub_main.c SourceLine:356] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:symbol_close SourceFile:drivers/usb/serial/symbolserial.c SourceLine:113] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:synusb_close SourceFile:drivers/input/mouse/synaptics_usb.c SourceLine:263] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:synusb_pre_reset SourceFile:drivers/input/mouse/synaptics_usb.c SourceLine:487] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:synusb_suspend SourceFile:drivers/input/mouse/synaptics_usb.c SourceLine:459] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:thrustmaster_remove SourceFile:drivers/hid/hid-thrustmaster.c SourceLine:273] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:ti_close SourceFile:drivers/usb/serial/ti_usb_3410_5052.c SourceLine:767] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:ti_close SourceFile:drivers/usb/serial/ti_usb_3410_5052.c SourceLine:768] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:ti_close SourceFile:drivers/usb/serial/ti_usb_3410_5052.c SourceLine:784] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:ti_open SourceFile:drivers/usb/serial/ti_usb_3410_5052.c SourceLine:748] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:tower_release SourceFile:drivers/usb/misc/legousbtower.c SourceLine:423] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:tower_release SourceFile:drivers/usb/misc/legousbtower.c SourceLine:424] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:ttusb_dec_exit_usb SourceFile:drivers/media/usb/ttusb-dec/ttusb_dec.c SourceLine:1574] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:ttusb_dec_exit_usb SourceFile:drivers/media/usb/ttusb-dec/ttusb_dec.c SourceLine:1585] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:ttusb_dec_start_iso_xfer SourceFile:drivers/media/usb/ttusb-dec/ttusb_dec.c SourceLine:947] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:ttusb_dec_stop_iso_xfer SourceFile:drivers/media/usb/ttusb-dec/ttusb_dec.c SourceLine:882] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:ttusb_stop_iso_xfer SourceFile:drivers/media/usb/ttusb-budget/dvb-ttusb-budget.c SourceLine:787] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:ttusbir_disconnect SourceFile:drivers/media/rc/ttusbir.c SourceLine:372] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:ttusbir_disconnect SourceFile:drivers/media/rc/ttusbir.c SourceLine:377] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:ttusbir_probe SourceFile:drivers/media/rc/ttusbir.c SourceLine:346] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:ttusbir_probe SourceFile:drivers/media/rc/ttusbir.c SourceLine:351] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:ttusbir_suspend SourceFile:drivers/media/rc/ttusbir.c SourceLine:391] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:ttusbir_suspend SourceFile:drivers/media/rc/ttusbir.c SourceLine:394] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:u2fzero_recv SourceFile:drivers/hid/hid-u2fzero.c SourceLine:157] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:uas_eh_abort_handler SourceFile:drivers/usb/storage/uas.c SourceLine:764] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:uas_eh_abort_handler SourceFile:drivers/usb/storage/uas.c SourceLine:768] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:uea_boot SourceFile:drivers/usb/atm/ueagle-atm.c SourceLine:2195] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:uea_stop SourceFile:drivers/usb/atm/ueagle-atm.c SourceLine:2217] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:unlink1 SourceFile:drivers/usb/misc/usbtest.c SourceLine:1500] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:unlink_all_urbs SourceFile:drivers/net/usb/pegasus.c SourceLine:808] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:unlink_all_urbs SourceFile:drivers/net/usb/pegasus.c SourceLine:809] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:unlink_all_urbs SourceFile:drivers/net/usb/pegasus.c SourceLine:810] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:unlink_all_urbs SourceFile:drivers/net/usb/rtl8150.c SourceLine:358] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:unlink_all_urbs SourceFile:drivers/net/usb/rtl8150.c SourceLine:359] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:unlink_all_urbs SourceFile:drivers/net/usb/rtl8150.c SourceLine:360] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:usb6fire_pcm_stream_stop SourceFile:sound/usb/6fire/pcm.c SourceLine:141] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:usb6fire_pcm_stream_stop SourceFile:sound/usb/6fire/pcm.c SourceLine:142] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:usb_acecad_close SourceFile:drivers/input/tablet/acecad.c SourceLine:108] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:usb_hcd_flush_endpoint SourceFile:drivers/usb/core/hcd.c SourceLine:1822] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:usb_kill_anchored_urbs SourceFile:drivers/usb/core/urb.c SourceLine:826] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:usb_onetouch_close SourceFile:drivers/usb/storage/onetouch.c SourceLine:137] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:usb_onetouch_pm_hook SourceFile:drivers/usb/storage/onetouch.c SourceLine:149] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:usb_serial_generic_close SourceFile:drivers/usb/serial/generic.c SourceLine:125] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:usb_serial_generic_close SourceFile:drivers/usb/serial/generic.c SourceLine:133] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:usb_serial_generic_submit_read_urbs SourceFile:drivers/usb/serial/generic.c SourceLine:338] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:usb_start_wait_urb SourceFile:drivers/usb/core/message.c SourceLine:74] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:usb_stor_msg_common SourceFile:drivers/usb/storage/transport.c SourceLine:174] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:usb_stream_stop SourceFile:sound/usb/usx2y/usb_stream.c SourceLine:770] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:usb_stream_stop SourceFile:sound/usb/usx2y/usb_stream.c SourceLine:771] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:usb_urb_kill SourceFile:drivers/media/usb/dvb-usb/usb-urb.c SourceLine:73] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:usb_urb_killv2 SourceFile:drivers/media/usb/dvb-usb-v2/usb_urb.c SourceLine:85] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:usb_wwan_close SourceFile:drivers/usb/serial/usb_wwan.c SourceLine:410] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:usb_wwan_close SourceFile:drivers/usb/serial/usb_wwan.c SourceLine:412] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:usb_wwan_close SourceFile:drivers/usb/serial/usb_wwan.c SourceLine:413] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:usbatm_usb_disconnect SourceFile:drivers/usb/atm/usbatm.c SourceLine:1238] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:usbdux_unlink_urbs SourceFile:drivers/comedi/drivers/usbdux.c SourceLine:211] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:usbduxfast_ai_stop SourceFile:drivers/comedi/drivers/usbduxfast.c SourceLine:204] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:usbduxfast_detach SourceFile:drivers/comedi/drivers/usbduxfast.c SourceLine:994] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:usbduxsigma_pwm_stop SourceFile:drivers/comedi/drivers/usbduxsigma.c SourceLine:994] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:usbduxsigma_unlink_urbs SourceFile:drivers/comedi/drivers/usbduxsigma.c SourceLine:174] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:usbduxsub_unlink_pwm_urbs SourceFile:drivers/comedi/drivers/usbdux.c SourceLine:1121] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:usbfs_start_wait_urb SourceFile:drivers/usb/core/devio.c SourceLine:1156] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:usbhid_close SourceFile:drivers/hid/usbhid/hid-core.c SourceLine:758] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:usbhid_init_reports SourceFile:drivers/hid/usbhid/hid-core.c SourceLine:789] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:usbhid_init_reports SourceFile:drivers/hid/usbhid/hid-core.c SourceLine:791] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:usbhid_stop SourceFile:drivers/hid/usbhid/hid-core.c SourceLine:1256] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:usbhid_stop SourceFile:drivers/hid/usbhid/hid-core.c SourceLine:1257] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:usbhid_stop SourceFile:drivers/hid/usbhid/hid-core.c SourceLine:1258] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:usbnet_disconnect SourceFile:drivers/net/usb/usbnet.c SourceLine:1698] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:usbnet_status_stop SourceFile:drivers/net/usb/usbnet.c SourceLine:305] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:usbpn_close SourceFile:drivers/net/usb/cdc-phonet.c SourceLine:253] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:usbtmc_free_int SourceFile:drivers/usb/class/usbtmc.c SourceLine:2366] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:usbtmc_suspend SourceFile:drivers/usb/class/usbtmc.c SourceLine:2546] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:usbtouch_close SourceFile:drivers/input/touchscreen/usbtouchscreen.c SourceLine:1363] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:usbtouch_suspend SourceFile:drivers/input/touchscreen/usbtouchscreen.c SourceLine:1377] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:usbtv_audio_stop SourceFile:drivers/media/usb/usbtv/usbtv-audio.c SourceLine:245] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:usbtv_audio_suspend SourceFile:drivers/media/usb/usbtv/usbtv-audio.c SourceLine:259] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:usbtv_stop SourceFile:drivers/media/usb/usbtv/usbtv-video.c SourceLine:545] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:ushc_disconnect SourceFile:drivers/mmc/host/ushc.c SourceLine:538] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:ushc_disconnect SourceFile:drivers/mmc/host/ushc.c SourceLine:539] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:ushc_disconnect SourceFile:drivers/mmc/host/ushc.c SourceLine:540] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:ushc_disconnect SourceFile:drivers/mmc/host/ushc.c SourceLine:541] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:usx2y_format_set SourceFile:sound/usb/usx2y/usbusx2yaudio.c SourceLine:731] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:usx2y_hwdep_urb_release SourceFile:sound/usb/usx2y/usx2yhwdeppcm.c SourceLine:266] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:usx2y_rate_set SourceFile:sound/usb/usx2y/usbusx2yaudio.c SourceLine:701] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:usx2y_unlinkseq SourceFile:sound/usb/usx2y/usbusx2y.c SourceLine:333] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:usx2y_urb_release SourceFile:sound/usb/usx2y/usbusx2yaudio.c SourceLine:368] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:usx2y_urbs_allocate SourceFile:sound/usb/usx2y/usbusx2yaudio.c SourceLine:418] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:usx2y_usbpcm_urbs_allocate SourceFile:sound/usb/usx2y/usx2yhwdeppcm.c SourceLine:333] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:uvc_status_stop SourceFile:drivers/media/usb/uvc/uvc_status.c SourceLine:347] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:visor_close SourceFile:drivers/usb/serial/visor.c SourceLine:254] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:vub300_usb_bulk_msg SourceFile:drivers/mmc/host/vub300.c SourceLine:1418] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:xbox_remote_disconnect SourceFile:drivers/media/rc/xbox_remote.c SourceLine:293] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:xbox_remote_probe SourceFile:drivers/media/rc/xbox_remote.c SourceLine:266] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:xbox_remote_rc_close SourceFile:drivers/media/rc/xbox_remote.c SourceLine:83] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:xpad360w_start_input SourceFile:drivers/input/joystick/xpad.c SourceLine:1856] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:xpad360w_stop_input SourceFile:drivers/input/joystick/xpad.c SourceLine:1865] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:xpad_start_input SourceFile:drivers/input/joystick/xpad.c SourceLine:1778] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:xpad_stop_input SourceFile:drivers/input/joystick/xpad.c SourceLine:1809] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:yurex_delete SourceFile:drivers/usb/misc/yurex.c SourceLine:97] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:yurex_delete SourceFile:drivers/usb/misc/yurex.c SourceLine:104] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:yurex_write SourceFile:drivers/usb/misc/yurex.c SourceLine:503]] TruncatedOutput:false]
|
| 46/2 |
2026/05/16 14:10 |
llm |
debugger |
0m
Model:
Tokens:
input: 86885
output: 42
thoughts: 8256
Thoughts: **Analyzing the Concurrency Bug in the cm109 Driver**
Okay, so I've been tracing the execution flow of the `cm109` driver to pinpoint a race condition that's causing a "double add" bug, and a list corruption that crashes the system. I see that the problem stems from concurrent access to the `urb_ctl` USB transfer request.
My initial thought was, how is `cm109_stop_traffic` called and when does it interact with `usb_kill_urb`? I then followed the control flow, realizing that `cm109_input_close` eventually leads to killing the URB through `cm109_stop_traffic`. This sets the stage for potential issues if these actions overlap with other URB submissions.
The critical piece of this puzzle lies in the interaction between `cm109_input_open` and `cm109_input_ev`. I determined that `cm109_input_open` *doesn't* acquire the critical `dev->ctl_submit_lock`, while `cm109_input_ev` does. That means these functions can be called concurrently. `cm109_input_ev` calls `cm109_toggle_buzzer_async`, which may submit `urb_ctl`. Then, if `cm109_input_open` is called before the URB completes, we've got a potential double-submission. The `usb_submit_urb` call has a check for a non-NULL `urb->hcpriv`, which *should* prevent this, but the check is unprotected by a lock. If `usb_submit_urb` is called at the same time by two concurrent callers, the double submission becomes possible.
I verified the code and discovered that `cm109_input_ev` gets called even when the device isn't opened by any user-space program. This means it can start the buzzer logic even before `cm109_input_open` is called, further amplifying the concurrency issue.
The problem, then, is this sequence: `cm109_input_open` and `cm109_input_ev` call `usb_submit_urb` concurrently, resulting in a race condition due to the unprotected `urb->hcpriv` check. The same URB is added twice to the endpoint's list, leading to a crash.
I also considered a few other potential races, specifically with `cm109_submit_ctl` and `cm109_submit_buzz_toggle`, but they are all serialized by the `dev->ctl_submit_lock`. So the concurrency issues are specifically in the area of `cm109_input_ev` and `cm109_input_open`.
To fix this, I can't simply take the lock in `cm109_input_open`. Using a spinlock with `GFP_KERNEL` will not work. But I could change to `GFP_ATOMIC` and protect the submission with a lock. Then, I considered that if `urb_ctl` is already pending, what do I do in `cm109_input_open`? Just fail? The device won't initialize, which isn't a great solution.
So the main problem is the lack of locking in `cm109_input_open`, combined with the unprotected check in `usb_submit_urb`. The root cause is the event injection via `KIOCSOUND` into `input_handle_event` that is not controlled by `dev->open`.
|
| 47/2 |
2026/05/16 14:10 |
tool |
codesearch-definition-source |
0m
Error:
Args: map[ContextFile:drivers/input/misc/yealink.c IncludeLines:true Name:input_ev]
Results: map[SourceCode: SourceFile:]
|
| 48/2 |
2026/05/16 14:10 |
llm |
debugger |
0m
Model:
Tokens:
input: 95980
output: 16
thoughts: 16
Thoughts: Okay, here's my interpretation of those thoughts, crafted with the mindset of an expert in the field:
**Diving into Yealink Event Handling**
Right, let's get down to brass tacks. My initial thought is to zero in on `yealink.c`. Specifically, I want to drill down and understand the implementation details of the `event` callback function within that file. This isn't just a casual glance; I need to quickly grasp how Yealink devices signal events, process them, and how my own code interacts at that level. I need to be able to assess it without losing too much time. This will give me a foothold to understand and diagnose the specific issue at hand.
|
| 49/2 |
2026/05/16 14:10 |
tool |
grepper |
0m
Args:
Results: map[Output:
Full output is too long, showing 500 out of 18115 lines.
Use more precise expression if possible.
[Documentation/accounting/psi.rst=121=Userspace monitor usage example
--
Documentation/accounting/psi.rst-147- }
Documentation/accounting/psi.rst:148: fds.events = POLLPRI;
Documentation/accounting/psi.rst-149-
--
Documentation/admin-guide/cgroup-v1/cpusets.rst=761=In this directory you can find several files::
--
Documentation/admin-guide/cgroup-v1/cpusets.rst-764- cgroup.clone_children cpuset.memory_pressure
Documentation/admin-guide/cgroup-v1/cpusets.rst:765: cgroup.event_control cpuset.memory_spread_page
Documentation/admin-guide/cgroup-v1/cpusets.rst-766- cgroup.procs cpuset.memory_spread_slab
--
Documentation/admin-guide/cgroup-v1/memory.rst=61=Brief summary of control files.
--
Documentation/admin-guide/cgroup-v1/memory.rst-66- cgroup.procs show list of processes
Documentation/admin-guide/cgroup-v1/memory.rst:67: cgroup.event_control an interface for event_fd()
Documentation/admin-guide/cgroup-v1/memory.rst-68- This knob is not available on CONFIG_PREEMPT_RT systems.
--
Documentation/admin-guide/cgroup-v1/memory.rst=767=To register a threshold, an application must:
--
Documentation/admin-guide/cgroup-v1/memory.rst-771-- write string like "<event_fd> <fd of memory.usage_in_bytes> <threshold>" to
Documentation/admin-guide/cgroup-v1/memory.rst:772: cgroup.event_control.
Documentation/admin-guide/cgroup-v1/memory.rst-773-
--
Documentation/admin-guide/cgroup-v1/memory.rst=792=To register a notifier, an application must:
--
Documentation/admin-guide/cgroup-v1/memory.rst-796- - write string like "<event_fd> <fd of memory.oom_control>" to
Documentation/admin-guide/cgroup-v1/memory.rst:797: cgroup.event_control
Documentation/admin-guide/cgroup-v1/memory.rst-798-
--
Documentation/admin-guide/cgroup-v1/memory.rst=894=register a notification, an application must:
--
Documentation/admin-guide/cgroup-v1/memory.rst-898-- write string as "<event_fd> <fd of memory.pressure_level> <level[,mode]>"
Documentation/admin-guide/cgroup-v1/memory.rst:899: to cgroup.event_control.
Documentation/admin-guide/cgroup-v1/memory.rst-900-
--
Documentation/admin-guide/cgroup-v1/pids.rst=35=superset of parent/child/pids.current.
Documentation/admin-guide/cgroup-v1/pids.rst-36-
Documentation/admin-guide/cgroup-v1/pids.rst:37:The pids.events file contains event counters:
Documentation/admin-guide/cgroup-v1/pids.rst-38-
--
Documentation/admin-guide/cgroup-v2.rst=185=cgroup v2 currently supports the following mount options.
--
Documentation/admin-guide/cgroup-v2.rst-202- memory_localevents
Documentation/admin-guide/cgroup-v2.rst:203: Only populate memory.events with data for the current cgroup,
Documentation/admin-guide/cgroup-v2.rst-204- and not any subtrees. This is legacy behaviour, the default
--
Documentation/admin-guide/cgroup-v2.rst-249- pids_localevents
Documentation/admin-guide/cgroup-v2.rst:250: The option restores v1-like behavior of pids.events:max, that is only
Documentation/admin-guide/cgroup-v2.rst-251- local (inside cgroup proper) fork failures are counted. Without this
Documentation/admin-guide/cgroup-v2.rst:252: option pids.events.max represents any pids.max enforcemnt across
Documentation/admin-guide/cgroup-v2.rst-253- cgroup's subtree.
--
Documentation/admin-guide/cgroup-v2.rst=411=in a threaded cgroup::
--
Documentation/admin-guide/cgroup-v2.rst-420-
Documentation/admin-guide/cgroup-v2.rst:421:Each non-root cgroup has a "cgroup.events" file which contains
Documentation/admin-guide/cgroup-v2.rst-422-"populated" field indicating whether the cgroup's sub-hierarchy has
--
Documentation/admin-guide/cgroup-v2.rst=436=process in C exits, B and C's "populated" fields would flip to "0" and
Documentation/admin-guide/cgroup-v2.rst:437:file modified events will be generated on the "cgroup.events" files of
Documentation/admin-guide/cgroup-v2.rst-438-both cgroups.
--
Documentation/admin-guide/cgroup-v2.rst=858=All cgroup core files are prefixed with "cgroup."
--
Documentation/admin-guide/cgroup-v2.rst-954-
Documentation/admin-guide/cgroup-v2.rst:955: cgroup.events
Documentation/admin-guide/cgroup-v2.rst-956- A read-only flat-keyed file which exists on non-root cgroups.
--
Documentation/admin-guide/cgroup-v2.rst-1032- unfrozen. Freezing of the cgroup may take some time; when this action
Documentation/admin-guide/cgroup-v2.rst:1033: is completed, the "frozen" value in the cgroup.events control file
Documentation/admin-guide/cgroup-v2.rst-1034- will be updated to "1" and the corresponding notification will be
--
Documentation/admin-guide/cgroup-v2.rst=1426=The following nested keys are defined.
--
Documentation/admin-guide/cgroup-v2.rst-1467-
Documentation/admin-guide/cgroup-v2.rst:1468: memory.events
Documentation/admin-guide/cgroup-v2.rst-1469- A read-only flat-keyed file which exists on non-root cgroups.
--
Documentation/admin-guide/cgroup-v2.rst-1476- hierarchy. For the local events at the cgroup level see
Documentation/admin-guide/cgroup-v2.rst:1477: memory.events.local.
Documentation/admin-guide/cgroup-v2.rst-1478-
--
Documentation/admin-guide/cgroup-v2.rst-1516-
Documentation/admin-guide/cgroup-v2.rst:1517: memory.events.local
Documentation/admin-guide/cgroup-v2.rst:1518: Similar to memory.events but the fields in the file are local
Documentation/admin-guide/cgroup-v2.rst-1519- to the cgroup i.e. not hierarchical. The file modified event
--
Documentation/admin-guide/cgroup-v2.rst-1852-
Documentation/admin-guide/cgroup-v2.rst:1853: memory.swap.events
Documentation/admin-guide/cgroup-v2.rst-1854- A read-only flat-keyed file which exists on non-root cgroups.
--
Documentation/admin-guide/cgroup-v2.rst=1959=use memory_recursiveprot. Configuring all descendants of a parent with finite
Documentation/admin-guide/cgroup-v2.rst:1960:protection to "max" works but it may unnecessarily skew memory.events:low
Documentation/admin-guide/cgroup-v2.rst-1961-field.
--
Documentation/admin-guide/cgroup-v2.rst=2395=PID Interface Files
--
Documentation/admin-guide/cgroup-v2.rst-2415-
Documentation/admin-guide/cgroup-v2.rst:2416: pids.events
Documentation/admin-guide/cgroup-v2.rst-2417- A read-only flat-keyed file which exists on non-root cgroups. Unless
--
Documentation/admin-guide/cgroup-v2.rst-2424-
Documentation/admin-guide/cgroup-v2.rst:2425: pids.events.local
Documentation/admin-guide/cgroup-v2.rst:2426: Similar to pids.events but the fields in the file are local
Documentation/admin-guide/cgroup-v2.rst-2427- to the cgroup i.e. not hierarchical. The file modified event
--
Documentation/admin-guide/cgroup-v2.rst=2837=HugeTLB Interface Files
--
Documentation/admin-guide/cgroup-v2.rst-2847-
Documentation/admin-guide/cgroup-v2.rst:2848: hugetlb.<hugepagesize>.events
Documentation/admin-guide/cgroup-v2.rst-2849- A read-only flat-keyed file which exists on non-root cgroups.
--
Documentation/admin-guide/cgroup-v2.rst-2853-
Documentation/admin-guide/cgroup-v2.rst:2854: hugetlb.<hugepagesize>.events.local
Documentation/admin-guide/cgroup-v2.rst:2855: Similar to hugetlb.<hugepagesize>.events but the fields in the file
Documentation/admin-guide/cgroup-v2.rst-2856- are local to the cgroup i.e. not hierarchical. The file modified event
--
Documentation/admin-guide/cgroup-v2.rst=2884=Miscellaneous controller provides 3 interface files. If two misc resources (res_a and res_b) are registered then:
--
Documentation/admin-guide/cgroup-v2.rst-2930-
Documentation/admin-guide/cgroup-v2.rst:2931: misc.events
Documentation/admin-guide/cgroup-v2.rst-2932- A read-only flat-keyed file which exists on non-root cgroups. The
--
Documentation/admin-guide/cgroup-v2.rst-2940-
Documentation/admin-guide/cgroup-v2.rst:2941: misc.events.local
Documentation/admin-guide/cgroup-v2.rst:2942: Similar to misc.events but the fields in the file are local to the
Documentation/admin-guide/cgroup-v2.rst-2943- cgroup i.e. not hierarchical. The file modified event generated on
--
Documentation/admin-guide/kernel-parameters.txt=97=Kernel parameters
--
Documentation/admin-guide/kernel-parameters.txt-8617-
Documentation/admin-guide/kernel-parameters.txt:8618: xen.event_eoi_delay= [XEN]
Documentation/admin-guide/kernel-parameters.txt-8619- How long to delay EOI handling in case of event
--
Documentation/admin-guide/kernel-parameters.txt-8621-
Documentation/admin-guide/kernel-parameters.txt:8622: xen.event_loop_timeout= [XEN]
Documentation/admin-guide/kernel-parameters.txt-8623- After which time (jiffies) the event handling loop
--
Documentation/networking/msg_zerocopy.rst=124=events field. Errors are signaled unconditionally.
--
Documentation/networking/msg_zerocopy.rst-128- pfd.fd = fd;
Documentation/networking/msg_zerocopy.rst:129: pfd.events = 0;
Documentation/networking/msg_zerocopy.rst-130- if (poll(&pfd, 1, -1) != 1 || pfd.revents & POLLERR == 0)
--
Documentation/networking/packet_mmap.rst=489=packets are in the ring::
--
Documentation/networking/packet_mmap.rst-494- pfd.revents = 0;
Documentation/networking/packet_mmap.rst:495: pfd.events = POLLIN|POLLRDNORM|POLLERR;
Documentation/networking/packet_mmap.rst-496-
--
Documentation/networking/packet_mmap.rst=529=The user can also use poll() to check if a buffer is available:
--
Documentation/networking/packet_mmap.rst-537- pfd.revents = 0;
Documentation/networking/packet_mmap.rst:538: pfd.events = POLLOUT;
Documentation/networking/packet_mmap.rst-539- retval = poll(&pfd, 1, timeout);
--
Documentation/networking/packet_mmap.rst=773=it with gcc -Wall -O2 blob.c, and try things like "./a.out eth0", etc.)::
--
Documentation/networking/packet_mmap.rst-972- pfd.fd = fd;
Documentation/networking/packet_mmap.rst:973: pfd.events = POLLIN | POLLERR;
Documentation/networking/packet_mmap.rst-974- pfd.revents = 0;
--
Documentation/networking/timestamping.rst=519=POLLERR in pollfd.revents if any data is ready on the error queue.
Documentation/networking/timestamping.rst:520:There is no need to pass this flag in pollfd.events. This flag is
Documentation/networking/timestamping.rst-521-ignored on request. See also `man 2 poll`.
--
Documentation/process/kernel-docs.rst=223=Rust
--
Documentation/process/kernel-docs.rst-354- :Title: Rust microconference
Documentation/process/kernel-docs.rst:355: :URL: https://lpc.events/event/18/sessions/186/#20240918
Documentation/process/kernel-docs.rst-356- :Title: Rust for Linux
Documentation/process/kernel-docs.rst:357: :URL: https://lpc.events/event/18/contributions/1912/
Documentation/process/kernel-docs.rst-358- :Title: Journey of a C kernel engineer starting a Rust driver project
Documentation/process/kernel-docs.rst:359: :URL: https://lpc.events/event/18/contributions/1911/
Documentation/process/kernel-docs.rst-360- :Title: Crafting a Linux kernel scheduler that runs in user-space
Documentation/process/kernel-docs.rst-361- using Rust
Documentation/process/kernel-docs.rst:362: :URL: https://lpc.events/event/18/contributions/1723/
Documentation/process/kernel-docs.rst-363- :Title: openHCL: A Linux and Rust based paravisor
Documentation/process/kernel-docs.rst:364: :URL: https://lpc.events/event/18/contributions/1956/
Documentation/process/kernel-docs.rst-365- :Keywords: rust, lpc, presentations.
--
Documentation/trace/boottime-trace.rst=164=ftrace.[instance.INSTANCE.]event.GROUP.EVENT.hist.[N.]onmatch.[M.]trace = EVENT[, ARG1[...]]
--
Documentation/trace/boottime-trace.rst-166- EVENT must be a synthetic event name, and ARG1... are parameters
Documentation/trace/boottime-trace.rst:167: for that event. Mandatory if 'onmatch.event' option is set.
Documentation/trace/boottime-trace.rst-168-
--
Documentation/trace/boottime-trace.rst=211=below::
Documentation/trace/boottime-trace.rst-212-
Documentation/trace/boottime-trace.rst:213: ftrace.event {
Documentation/trace/boottime-trace.rst-214- task.task_newtask {
--
Documentation/trace/histogram-design.rst=710=variable it references. If a variable reference was created using the
Documentation/trace/histogram-design.rst:711:explicit system.event.$var_ref notation, the hist_field's system and
Documentation/trace/histogram-design.rst-712-event_name variables are also set.
--
Documentation/trace/histogram-design.rst=1191=and event name for the onmatch() handler::
--
Documentation/trace/histogram-design.rst-1311-
Documentation/trace/histogram-design.rst:1312: hist_data->actions[0].match_data.event_system: sched
Documentation/trace/histogram-design.rst:1313: hist_data->actions[0].match_data.event: sched_waking
Documentation/trace/histogram-design.rst-1314-
--
Documentation/trace/histogram-design.rst=1763=the details of which are in the field variables section::
--
Documentation/trace/histogram-design.rst-1869-
Documentation/trace/histogram-design.rst:1870: hist_data->actions[0].match_data.event_system: sched
Documentation/trace/histogram-design.rst:1871: hist_data->actions[0].match_data.event: sched_waking
Documentation/trace/histogram-design.rst-1872-
--
Documentation/trace/histogram-design.rst=1986=$woken_pid slot in the trace event invocation::
--
Documentation/trace/histogram-design.rst-2114-
Documentation/trace/histogram-design.rst:2115: hist_data->actions[0].match_data.event_system: sched
Documentation/trace/histogram-design.rst:2116: hist_data->actions[0].match_data.event: sched_waking
Documentation/trace/histogram-design.rst-2117-
--
Documentation/trace/histogram.rst=2131=The available handlers are:
Documentation/trace/histogram.rst-2132-
Documentation/trace/histogram.rst:2133: - onmatch(matching.event) - invoke action on any addition or update
Documentation/trace/histogram.rst-2134- - onmax(var) - invoke action if var exceeds current max
--
Documentation/trace/histogram.rst=2143=The following commonly-used handler.action pairs are available:
Documentation/trace/histogram.rst-2144-
Documentation/trace/histogram.rst:2145: - onmatch(matching.event).trace(<synthetic_event_name>,param list)
Documentation/trace/histogram.rst-2146-
Documentation/trace/histogram.rst:2147: The 'onmatch(matching.event).trace(<synthetic_event_name>,param
Documentation/trace/histogram.rst-2148- list)' hist trigger action is invoked whenever an event matches
--
Documentation/trace/histogram.rst-2165-
Documentation/trace/histogram.rst:2166: onmatch(matching.event).<synthetic_event_name>(param list)
Documentation/trace/histogram.rst-2167-
--
Documentation/trace/histogram.rst-2169- parameters which may be either variables or fields defined on
Documentation/trace/histogram.rst:2170: either the 'matching.event' or the target event. The variables or
Documentation/trace/histogram.rst-2171- fields specified in the param list may be either fully-qualified
--
Documentation/trace/histogram.rst-2175- must be fully qualified if it refers to the matching event. A
Documentation/trace/histogram.rst:2176: fully-qualified name is of the form 'system.event_name.$var_name'
Documentation/trace/histogram.rst:2177: or 'system.event_name.field'.
Documentation/trace/histogram.rst-2178-
Documentation/trace/histogram.rst:2179: The 'matching.event' specification is simply the fully qualified
Documentation/trace/histogram.rst-2180- event name of the event that matches the target event for the
Documentation/trace/histogram.rst:2181: onmatch() functionality, in the form 'system.event_name'. Histogram
Documentation/trace/histogram.rst-2182- keys of both events are compared to find if events match. In case
--
Documentation/trace/rv/deterministic_automata.rst=55=For example, the 'wip' automata can be presented as (augmented with comments)::
--
Documentation/trace/rv/deterministic_automata.rst-86- },
Documentation/trace/rv/deterministic_automata.rst:87: .event_names = {
Documentation/trace/rv/deterministic_automata.rst-88- "preempt_disable",
--
Documentation/trace/rv/hybrid_automata.rst=191=This is a combination of both iterations of the stall example::
--
Documentation/trace/rv/hybrid_automata.rst-232- },
Documentation/trace/rv/hybrid_automata.rst:233: .event_names = {
Documentation/trace/rv/hybrid_automata.rst-234- "dequeue",
--
Documentation/translations/zh_CN/accounting/psi.rst=87=psi接口提供的均值即可。
--
Documentation/translations/zh_CN/accounting/psi.rst-117- }
Documentation/translations/zh_CN/accounting/psi.rst:118: fds.events = POLLPRI;
Documentation/translations/zh_CN/accounting/psi.rst-119-
--
Documentation/translations/zh_CN/networking/msg_zerocopy.rst=101=MSG_ZEROCOPY 标志的 send 调用成功发送数据时,计数器都会增加。如果调用失败或长度为零,
--
Documentation/translations/zh_CN/networking/msg_zerocopy.rst-116- pfd.fd = fd;
Documentation/translations/zh_CN/networking/msg_zerocopy.rst:117: pfd.events = 0;
Documentation/translations/zh_CN/networking/msg_zerocopy.rst-118- if (poll(&pfd, 1, -1) != 1 || pfd.revents & POLLERR == 0)
--
Documentation/translations/zh_CN/networking/timestamping.rst=416=select。poll() 将在 pollfd.revents 中返回 POLLERR,如果错误队列
Documentation/translations/zh_CN/networking/timestamping.rst:417:中有数据。没有必要在 pollfd.events中传递此标志。此标志在请求时被忽
Documentation/translations/zh_CN/networking/timestamping.rst-418-略。另请参阅 `man 2 poll`。
--
Documentation/usb/gadget_printer.rst=153=Example Code
--
Documentation/usb/gadget_printer.rst-217-
Documentation/usb/gadget_printer.rst:218: fd[0].events = POLLIN | POLLRDNORM;
Documentation/usb/gadget_printer.rst-219-
--
Documentation/usb/gadget_printer.rst-267-
Documentation/usb/gadget_printer.rst:268: fd[0].events = POLLOUT | POLLWRNORM;
Documentation/usb/gadget_printer.rst-269-
--
Documentation/userspace-api/gpio/gpio-v2-line-event-read.rst=57=creation, and can be influenced by the
Documentation/userspace-api/gpio/gpio-v2-line-event-read.rst:58::c:type:`request.event_buffer_size<gpio_v2_line_request>`.
Documentation/userspace-api/gpio/gpio-v2-line-event-read.rst-59-The default size is 16 times the number of lines requested.
--
Documentation/userspace-api/media/mediactl/request-api.rst=193=query values as soon as the capture buffer is produced.
--
Documentation/userspace-api/media/mediactl/request-api.rst-196-
Documentation/userspace-api/media/mediactl/request-api.rst:197: struct pollfd pfd = { .events = POLLPRI, .fd = req_fd };
Documentation/userspace-api/media/mediactl/request-api.rst-198- poll(&pfd, 1, -1);
--
arch/alpha/kernel/perf_event.c=215=static const struct alpha_pmu_t ev67_pmu = {
arch/alpha/kernel/perf_event.c:216: .event_map = ev67_perfmon_event_map,
arch/alpha/kernel/perf_event.c-217- .max_events = ARRAY_SIZE(ev67_perfmon_event_map),
--
arch/alpha/kernel/perf_event.c=340=static int collect_events(struct perf_event *group, int max_count,
--
arch/alpha/kernel/perf_event.c-350- event[n] = group;
arch/alpha/kernel/perf_event.c:351: evtype[n] = group->hw.event_base;
arch/alpha/kernel/perf_event.c-352- current_idx[n++] = PMC_NO_INDEX;
--
arch/alpha/kernel/perf_event.c-358- event[n] = pe;
arch/alpha/kernel/perf_event.c:359: evtype[n] = pe->hw.event_base;
arch/alpha/kernel/perf_event.c-360- current_idx[n++] = PMC_NO_INDEX;
--
arch/alpha/kernel/perf_event.c=433=static int alpha_pmu_add(struct perf_event *event, int flags)
--
arch/alpha/kernel/perf_event.c-458- cpuc->event[n0] = event;
arch/alpha/kernel/perf_event.c:459: cpuc->evtype[n0] = event->hw.event_base;
arch/alpha/kernel/perf_event.c-460- cpuc->current_idx[n0] = PMC_NO_INDEX;
--
arch/alpha/kernel/perf_event.c=755=static struct pmu pmu = {
--
arch/alpha/kernel/perf_event.c-757- .pmu_disable = alpha_pmu_disable,
arch/alpha/kernel/perf_event.c:758: .event_init = alpha_pmu_event_init,
arch/alpha/kernel/perf_event.c-759- .add = alpha_pmu_add,
--
arch/arc/kernel/perf_event.c=721=static int arc_pmu_device_probe(struct platform_device *pdev)
--
arch/arc/kernel/perf_event.c-790- .pmu_disable = arc_pmu_disable,
arch/arc/kernel/perf_event.c:791: .event_init = arc_pmu_event_init,
arch/arc/kernel/perf_event.c-792- .add = arc_pmu_add,
--
arch/arm/kernel/sys_oabi-compat.c=283=asmlinkage long sys_oabi_epoll_ctl(int epfd, int op, int fd,
--
arch/arm/kernel/sys_oabi-compat.c-292-
arch/arm/kernel/sys_oabi-compat.c:293: kernel.events = user.events;
arch/arm/kernel/sys_oabi-compat.c-294- kernel.data = user.data;
--
arch/arm/mach-imx/mmdc.c=435=static int mmdc_pmu_init(struct mmdc_pmu *pmu_mmdc,
--
arch/arm/mach-imx/mmdc.c-442- .attr_groups = attr_groups,
arch/arm/mach-imx/mmdc.c:443: .event_init = mmdc_pmu_event_init,
arch/arm/mach-imx/mmdc.c-444- .add = mmdc_pmu_event_add,
--
arch/arm/mm/cache-l2x0-pmu.c=503=static __init int l2x0_pmu_init(void)
--
arch/arm/mm/cache-l2x0-pmu.c-524- .del = l2x0_pmu_event_del,
arch/arm/mm/cache-l2x0-pmu.c:525: .event_init = l2x0_pmu_event_init,
arch/arm/mm/cache-l2x0-pmu.c-526- .attr_groups = l2x0_pmu_attr_groups,
--
arch/arm/plat-orion/time.c=169=static irqreturn_t orion_timer_interrupt(int irq, void *dev_id)
--
arch/arm/plat-orion/time.c-174- writel(bridge_timer1_clr_mask, bridge_base + BRIDGE_CAUSE_OFF);
arch/arm/plat-orion/time.c:175: orion_clkevt.event_handler(&orion_clkevt);
arch/arm/plat-orion/time.c-176-
--
arch/arm64/kernel/vmlinux.lds.S-19- __hyp_event_ids_start = .; \
arch/arm64/kernel/vmlinux.lds.S:20: *(HYP_SECTION_NAME(.event_ids)) \
arch/arm64/kernel/vmlinux.lds.S-21- __hyp_event_ids_end = .;
--
arch/arm64/kvm/hyp/include/nvhe/define_events.h-5- struct hyp_event_id hyp_event_id_##__name \
arch/arm64/kvm/hyp/include/nvhe/define_events.h:6: __section(".hyp.event_ids."#__name) = { \
arch/arm64/kvm/hyp/include/nvhe/define_events.h-7- .enabled = ATOMIC_INIT(0), \
--
arch/arm64/kvm/hyp/nvhe/hyp.lds.S=14=SECTIONS {
--
arch/arm64/kvm/hyp/nvhe/hyp.lds.S-20- . = ALIGN(PAGE_SIZE);
arch/arm64/kvm/hyp/nvhe/hyp.lds.S:21: BEGIN_HYP_SECTION(.event_ids)
arch/arm64/kvm/hyp/nvhe/hyp.lds.S:22: *(SORT(.hyp.event_ids.*))
arch/arm64/kvm/hyp/nvhe/hyp.lds.S-23- END_HYP_SECTION
--
arch/arm64/kvm/hyp/nvhe/switch.c=147=static bool __pmu_switch_to_guest(struct kvm_vcpu *vcpu)
arch/arm64/kvm/hyp/nvhe/switch.c-148-{
arch/arm64/kvm/hyp/nvhe/switch.c:149: struct kvm_pmu_events *pmu = &vcpu->arch.pmu.events;
arch/arm64/kvm/hyp/nvhe/switch.c-150-
--
arch/arm64/kvm/hyp/nvhe/switch.c=163=static void __pmu_switch_to_host(struct kvm_vcpu *vcpu)
arch/arm64/kvm/hyp/nvhe/switch.c-164-{
arch/arm64/kvm/hyp/nvhe/switch.c:165: struct kvm_pmu_events *pmu = &vcpu->arch.pmu.events;
arch/arm64/kvm/hyp/nvhe/switch.c-166-
--
arch/csky/kernel/perf_event.c=1197=int init_hw_perf_events(void)
--
arch/csky/kernel/perf_event.c-1208- .pmu_disable = csky_pmu_disable,
arch/csky/kernel/perf_event.c:1209: .event_init = csky_pmu_event_init,
arch/csky/kernel/perf_event.c-1210- .add = csky_pmu_add,
--
arch/loongarch/kernel/perf_event.c=530=static int loongarch_pmu_event_init(struct perf_event *event)
--
arch/loongarch/kernel/perf_event.c-546- /* Init it to avoid false validate_group */
arch/loongarch/kernel/perf_event.c:547: event->hw.event_base = 0xffffffff;
arch/loongarch/kernel/perf_event.c-548- return -ENOENT;
--
arch/loongarch/kernel/perf_event.c=573=static struct pmu pmu = {
--
arch/loongarch/kernel/perf_event.c-575- .pmu_disable = loongarch_pmu_disable,
arch/loongarch/kernel/perf_event.c:576: .event_init = loongarch_pmu_event_init,
arch/loongarch/kernel/perf_event.c-577- .add = loongarch_pmu_add,
--
arch/loongarch/kernel/perf_event.c=854=static const struct loongarch_perf_event *loongarch_pmu_map_raw_event(u64 config)
arch/loongarch/kernel/perf_event.c-855-{
arch/loongarch/kernel/perf_event.c:856: raw_event.event_id = M_PERFCTL_EVENT(config);
arch/loongarch/kernel/perf_event.c-857-
--
arch/mips/kernel/cevt-ds1287.c=82=static struct clock_event_device ds1287_clockevent = {
--
arch/mips/kernel/cevt-ds1287.c-88- .tick_resume = ds1287_shutdown,
arch/mips/kernel/cevt-ds1287.c:89: .event_handler = ds1287_event_handler,
arch/mips/kernel/cevt-ds1287.c-90-};
--
arch/mips/kernel/cevt-gt641xx.c=101=static struct clock_event_device gt641xx_timer0_clockevent = {
--
arch/mips/kernel/cevt-gt641xx.c-110- .tick_resume = gt641xx_timer0_shutdown,
arch/mips/kernel/cevt-gt641xx.c:111: .event_handler = gt641xx_timer0_event_handler,
arch/mips/kernel/cevt-gt641xx.c-112-};
--
arch/mips/kernel/i8253.c=14=static irqreturn_t timer_interrupt(int irq, void *dev_id)
arch/mips/kernel/i8253.c-15-{
arch/mips/kernel/i8253.c:16: i8253_clockevent.event_handler(&i8253_clockevent);
arch/mips/kernel/i8253.c-17-
--
arch/mips/kernel/perf_event_mipsxx.c=689=static struct pmu pmu = {
--
arch/mips/kernel/perf_event_mipsxx.c-691- .pmu_disable = mipspmu_disable,
arch/mips/kernel/perf_event_mipsxx.c:692: .event_init = mipspmu_event_init,
arch/mips/kernel/perf_event_mipsxx.c-693- .add = mipspmu_add,
--
arch/mips/kernel/perf_event_mipsxx.c=1703=static const struct mips_perf_event *mipsxx_pmu_map_raw_event(u64 config)
--
arch/mips/kernel/perf_event_mipsxx.c-1847-
arch/mips/kernel/perf_event_mipsxx.c:1848: raw_event.event_id = base_id;
arch/mips/kernel/perf_event_mipsxx.c-1849-
--
arch/mips/kernel/perf_event_mipsxx.c=1853=static const struct mips_perf_event *octeon_pmu_map_raw_event(u64 config)
--
arch/mips/kernel/perf_event_mipsxx.c-1859- raw_event.cntr_mask = CNTR_ALL;
arch/mips/kernel/perf_event_mipsxx.c:1860: raw_event.event_id = base_id;
arch/mips/kernel/perf_event_mipsxx.c-1861-
--
arch/mips/loongson2ef/common/cs5536/cs5536_mfgpt.c=83=static irqreturn_t timer_interrupt(int irq, void *dev_id)
--
arch/mips/loongson2ef/common/cs5536/cs5536_mfgpt.c-97-
arch/mips/loongson2ef/common/cs5536/cs5536_mfgpt.c:98: mfgpt_clockevent.event_handler(&mfgpt_clockevent);
arch/mips/loongson2ef/common/cs5536/cs5536_mfgpt.c-99-
--
arch/powerpc/perf/8xx-pmu.c=179=static struct pmu mpc8xx_pmu = {
arch/powerpc/perf/8xx-pmu.c:180: .event_init = mpc8xx_pmu_event_init,
arch/powerpc/perf/8xx-pmu.c-181- .add = mpc8xx_pmu_add,
--
arch/powerpc/perf/core-book3s.c=1590=static int collect_events(struct perf_event *group, int max_count,
--
arch/powerpc/perf/core-book3s.c-1600- ctrs[n] = group;
arch/powerpc/perf/core-book3s.c:1601: flags[n] = group->hw.event_base;
arch/powerpc/perf/core-book3s.c-1602- events[n++] = group->hw.config;
--
arch/powerpc/perf/core-book3s.c-1609- ctrs[n] = event;
arch/powerpc/perf/core-book3s.c:1610: flags[n] = event->hw.event_base;
arch/powerpc/perf/core-book3s.c-1611- events[n++] = event->hw.config;
--
arch/powerpc/perf/core-book3s.c=1623=static int power_pmu_add(struct perf_event *event, int ef_flags)
--
arch/powerpc/perf/core-book3s.c-1642- cpuhw->events[n0] = event->hw.config;
arch/powerpc/perf/core-book3s.c:1643: cpuhw->flags[n0] = event->hw.event_base;
arch/powerpc/perf/core-book3s.c-1644-
--
arch/powerpc/perf/core-book3s.c=2000=static int power_pmu_event_init(struct perf_event *event)
--
arch/powerpc/perf/core-book3s.c-2162- event->hw.config = events[n];
arch/powerpc/perf/core-book3s.c:2163: event->hw.event_base = cflags[n];
arch/powerpc/perf/core-book3s.c-2164- event->hw.last_period = event->hw.sample_period;
--
arch/powerpc/perf/core-book3s.c=2210=static struct pmu power_pmu = {
--
arch/powerpc/perf/core-book3s.c-2212- .pmu_disable = power_pmu_disable,
arch/powerpc/perf/core-book3s.c:2213: .event_init = power_pmu_event_init,
arch/powerpc/perf/core-book3s.c-2214- .add = power_pmu_add,
--
arch/powerpc/perf/core-book3s.c-2221- .commit_txn = power_pmu_commit_txn,
arch/powerpc/perf/core-book3s.c:2222: .event_idx = power_pmu_event_idx,
arch/powerpc/perf/core-book3s.c-2223- .sched_task = power_pmu_sched_task,
--
arch/powerpc/perf/core-fsl-emb.c=573=static struct pmu fsl_emb_pmu = {
--
arch/powerpc/perf/core-fsl-emb.c-575- .pmu_disable = fsl_emb_pmu_disable,
arch/powerpc/perf/core-fsl-emb.c:576: .event_init = fsl_emb_pmu_event_init,
arch/powerpc/perf/core-fsl-emb.c-577- .add = fsl_emb_pmu_add,
--
arch/powerpc/perf/hv-24x7.c=1636=static struct pmu h_24x7_pmu = {
--
arch/powerpc/perf/hv-24x7.c-1640- .attr_groups = attr_groups,
arch/powerpc/perf/hv-24x7.c:1641: .event_init = h_24x7_event_init,
arch/powerpc/perf/hv-24x7.c-1642- .add = h_24x7_event_add,
--
arch/powerpc/perf/hv-gpci.c=831=static struct pmu h_gpci_pmu = {
--
arch/powerpc/perf/hv-gpci.c-835- .attr_groups = attr_groups,
arch/powerpc/perf/hv-gpci.c:836: .event_init = h_gpci_event_init,
arch/powerpc/perf/hv-gpci.c-837- .add = h_gpci_event_add,
--
arch/powerpc/perf/imc-pmu.c=511=static int nest_imc_event_init(struct perf_event *event)
--
arch/powerpc/perf/imc-pmu.c-561- l_config = config & IMC_EVENT_OFFSET_MASK;
arch/powerpc/perf/imc-pmu.c:562: event->hw.event_base = (u64)pcni->vbase + l_config;
arch/powerpc/perf/imc-pmu.c-563- node_id = cpu_to_node(event->cpu);
--
]
]
|
| 50/2 |
2026/05/16 14:10 |
llm |
debugger |
0m
Model:
Tokens:
input: 107488
output: 39
thoughts: 41
Thoughts: Okay, here's my take on those thoughts, tailored for an expert audience:
**Analyzing the Input Event Handling**
Right, so `input_ev` immediately jumps out as non-standard. My initial assumption, looking at the term `input_ev`, is it should have been `event`. It's always the case, when I see something odd like this, the first thing I do is check the source of truth, specifically the header files defining core data structures and functions. In this case, I'd immediately drill into `include/linux/input.h` to examine the `struct input_dev` definition. That's where I can confirm the method name and its associated data members. That's the best way to uncover whether this is a naming convention or a deeper logical issue.
|
| 51/2 |
2026/05/16 14:10 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:include/linux/input.h IncludeLines:true Name:input_dev]
Results: map[SourceCode: 45: /**
46: * struct input_dev - represents an input device
47: * @name: name of the device
48: * @phys: physical path to the device in the system hierarchy
49: * @uniq: unique identification code for the device (if device has it)
50: * @id: id of the device (struct input_id)
51: * @propbit: bitmap of device properties and quirks
52: * @evbit: bitmap of types of events supported by the device (EV_KEY,
53: * EV_REL, etc.)
54: * @keybit: bitmap of keys/buttons this device has
55: * @relbit: bitmap of relative axes for the device
56: * @absbit: bitmap of absolute axes for the device
57: * @mscbit: bitmap of miscellaneous events supported by the device
58: * @ledbit: bitmap of leds present on the device
59: * @sndbit: bitmap of sound effects supported by the device
60: * @ffbit: bitmap of force feedback effects supported by the device
61: * @swbit: bitmap of switches present on the device
62: * @hint_events_per_packet: average number of events generated by the
63: * device in a packet (between EV_SYN/SYN_REPORT events). Used by
64: * event handlers to estimate size of the buffer needed to hold
65: * events.
66: * @keycodemax: size of keycode table
67: * @keycodesize: size of elements in keycode table
68: * @keycode: map of scancodes to keycodes for this device
69: * @getkeycode: optional legacy method to retrieve current keymap.
70: * @setkeycode: optional method to alter current keymap, used to implement
71: * sparse keymaps. If not supplied default mechanism will be used.
72: * The method is being called while holding event_lock and thus must
73: * not sleep
74: * @ff: force feedback structure associated with the device if device
75: * supports force feedback effects
76: * @poller: poller structure associated with the device if device is
77: * set up to use polling mode
78: * @repeat_key: stores key code of the last key pressed; used to implement
79: * software autorepeat
80: * @timer: timer for software autorepeat
81: * @rep: current values for autorepeat parameters (delay, rate)
82: * @mt: pointer to multitouch state
83: * @absinfo: array of &struct input_absinfo elements holding information
84: * about absolute axes (current value, min, max, flat, fuzz,
85: * resolution)
86: * @key: reflects current state of device's keys/buttons
87: * @led: reflects current state of device's LEDs
88: * @snd: reflects current state of sound effects
89: * @sw: reflects current state of device's switches
90: * @open: this method is called when the very first user calls
91: * input_open_device(). The driver must prepare the device
92: * to start generating events (start polling thread,
93: * request an IRQ, submit URB, etc.). The meaning of open() is
94: * to start providing events to the input core.
95: * @close: this method is called when the very last user calls
96: * input_close_device(). The meaning of close() is to stop
97: * providing events to the input core.
98: * @flush: purges the device. Most commonly used to get rid of force
99: * feedback effects loaded into the device when disconnecting
100: * from it
101: * @event: event handler for events sent _to_ the device, like EV_LED
102: * or EV_SND. The device is expected to carry out the requested
103: * action (turn on a LED, play sound, etc.) The call is protected
104: * by @event_lock and must not sleep
105: * @grab: input handle that currently has the device grabbed (via
106: * EVIOCGRAB ioctl). When a handle grabs a device it becomes sole
107: * recipient for all input events coming from the device
108: * @event_lock: this spinlock is taken when input core receives
109: * and processes a new event for the device (in input_event()).
110: * Code that accesses and/or modifies parameters of a device
111: * (such as keymap or absmin, absmax, absfuzz, etc.) after device
112: * has been registered with input core must take this lock.
113: * @mutex: serializes calls to open(), close() and flush() methods
114: * @users: stores number of users (input handlers) that opened this
115: * device. It is used by input_open_device() and input_close_device()
116: * to make sure that dev->open() is only called when the first
117: * user opens device and dev->close() is called when the very
118: * last user closes the device
119: * @going_away: marks devices that are in a middle of unregistering and
120: * causes input_open_device*() fail with -ENODEV.
121: * @dev: driver model's view of this device
122: * @h_list: list of input handles associated with the device. When
123: * accessing the list dev->mutex must be held
124: * @node: used to place the device onto input_dev_list
125: * @num_vals: number of values queued in the current frame
126: * @max_vals: maximum number of values queued in a frame
127: * @vals: array of values queued in the current frame
128: * @devres_managed: indicates that devices is managed with devres framework
129: * and needs not be explicitly unregistered or freed.
130: * @timestamp: storage for a timestamp set by input_set_timestamp called
131: * by a driver
132: * @inhibited: indicates that the input device is inhibited. If that is
133: * the case then input core ignores any events generated by the device.
134: * Device's close() is called when it is being inhibited and its open()
135: * is called when it is being uninhibited.
136: */
137: struct input_dev {
138: const char *name;
139: const char *phys;
140: const char *uniq;
141: struct input_id id;
142:
143: unsigned long propbit[BITS_TO_LONGS(INPUT_PROP_CNT)];
144:
145: unsigned long evbit[BITS_TO_LONGS(EV_CNT)];
146: unsigned long keybit[BITS_TO_LONGS(KEY_CNT)];
147: unsigned long relbit[BITS_TO_LONGS(REL_CNT)];
148: unsigned long absbit[BITS_TO_LONGS(ABS_CNT)];
149: unsigned long mscbit[BITS_TO_LONGS(MSC_CNT)];
150: unsigned long ledbit[BITS_TO_LONGS(LED_CNT)];
151: unsigned long sndbit[BITS_TO_LONGS(SND_CNT)];
152: unsigned long ffbit[BITS_TO_LONGS(FF_CNT)];
153: unsigned long swbit[BITS_TO_LONGS(SW_CNT)];
154:
155: unsigned int hint_events_per_packet;
156:
157: unsigned int keycodemax;
158: unsigned int keycodesize;
159: void *keycode;
160:
161: int (*setkeycode)(struct input_dev *dev,
162: const struct input_keymap_entry *ke,
163: unsigned int *old_keycode);
164: int (*getkeycode)(struct input_dev *dev,
165: struct input_keymap_entry *ke);
166:
167: struct ff_device *ff;
168:
169: struct input_dev_poller *poller;
170:
171: unsigned int repeat_key;
172: struct timer_list timer;
173:
174: int rep[REP_CNT];
175:
176: struct input_mt *mt;
177:
178: struct input_absinfo *absinfo;
179:
180: unsigned long key[BITS_TO_LONGS(KEY_CNT)];
181: unsigned long led[BITS_TO_LONGS(LED_CNT)];
182: unsigned long snd[BITS_TO_LONGS(SND_CNT)];
183: unsigned long sw[BITS_TO_LONGS(SW_CNT)];
184:
185: int (*open)(struct input_dev *dev);
186: void (*close)(struct input_dev *dev);
187: int (*flush)(struct input_dev *dev, struct file *file);
188: int (*event)(struct input_dev *dev, unsigned int type, unsigned int code, int value);
189:
190: struct input_handle __rcu *grab;
191:
192: spinlock_t event_lock;
193: struct mutex mutex;
194:
195: unsigned int users;
196: bool going_away;
197:
198: struct device dev;
199:
200: struct list_head h_list;
201: struct list_head node;
202:
203: unsigned int num_vals;
204: unsigned int max_vals;
205: struct input_value *vals;
206:
207: bool devres_managed;
208:
209: ktime_t timestamp[INPUT_CLK_MAX];
210:
211: bool inhibited;
212: };
SourceFile:include/linux/input.h]
|
| 52/2 |
2026/05/16 14:10 |
llm |
debugger |
0m
Model:
Tokens:
input: 110622
output: 50
thoughts: 9492
Thoughts: **Analyzing and Fixing the USB Driver Double-Add Issue**
Okay, so I've been digging into this USB driver code, and I've identified a race condition causing a double submission of URBs, leading to a crash. It all boils down to how the control URB (`urb_ctl`) is handled in relation to device open/close and events.
The key issue is that `cm109_input_ev` (called from `input_inject_event`) can submit the `urb_ctl` at any time, even concurrently with open/close operations. The core problem is that `cm109_input_ev` currently *doesn't* check if the device is actually open (`dev->open`) before it submits the URB. This means that after a device is closed, `cm109_input_ev` can restart the URB polling, which causes issues when opening the device, and causes the "double add".
Here's the breakdown of my thought process:
* **Concurrency:** `cm109_input_ev` is called with `@event_lock` held, but `cm109_input_open` and `cm109_input_ev` can run concurrently!
* **Double Submission:** To prevent double submissions, I considered using `dev->ctl_submit_lock`, but `usb_submit_urb` can sleep. Thus, I would either need to use `GFP_ATOMIC` or the ability to not hold a spinlock.
* **Data Corruption:** Modifying `dev->ctl_data` while `urb_ctl` is pending is a race condition. Thus, data corruption or USB errors can occur.
* **Initial Fix Idea:** I first thought about killing the URB in `cm109_input_open`, but that won't work because `cm109_input_ev` can submit a new one right after.
* **Flag for Open:** The correct solution involves using `dev->open` to gate `cm109_input_ev`. I need to ensure that `cm109_input_ev` *only* submits the URB if `dev->open` is 1. This prevents the polling from restarting after the device is closed. However, I have to ensure the correct setting order, so that all concurrent function calls correctly see the correct state. I need to make sure that the device is closed *before* I stop traffic.
* **Critical Order in Close:** The critical fix is to set `dev->open = 0` *before* calling `cm109_stop_traffic` (which kills the URBs). This ensures that `cm109_input_ev` will see `dev->open = 0` and won't restart the polling loop.
* **Suspend/Resume consideration:** The code might submit again during suspend, since `cm109_input_ev` does not check for `dev->resetting`. However, `dev->resetting` is set at the start and end of `cm109_usb_resume`, so it is safe. But `dev->resetting` isn't set during suspend, so `cm109_input_ev` *will* submit during suspend and can cause the double add.
Thus, the fix requires two modifications:
1. **Modify `cm109_input_ev`**: Add a check for `dev->open` and return immediately if it's 0.
2. **Modify `cm109_input_close`**: Set `dev->open = 0` before calling `cm109_stop_traffic`.
This ensures that the device doesn't try to submit an URB after it's been closed, and prevents the crash.
|
| 53/2 |
2026/05/16 14:10 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/input/misc/cm109.c IncludeLines:true Name:cm109_usb_suspend]
Results: map[SourceCode: 811: static int cm109_usb_suspend(struct usb_interface *intf, pm_message_t message)
812: {
813: struct cm109_dev *dev = usb_get_intfdata(intf);
814:
815: dev_info(&intf->dev, "cm109: usb_suspend (event=%d)\n", message.event);
816:
817: guard(mutex)(&dev->pm_mutex);
818:
819: cm109_stop_traffic(dev);
820:
821: return 0;
822: }
SourceFile:drivers/input/misc/cm109.c]
|
| 54/2 |
2026/05/16 14:10 |
llm |
debugger |
0m
Model:
Tokens:
input: 121210
output: 49
thoughts: 11
Thoughts: Okay, here's my summary, presented as a stream of thought:
**Diving into the `cm109_usb_resume` Functionality**
Alright, let's break down this `cm109_usb_resume` situation. My initial thought is, "What's the context here?" Am I dealing with a specific chipset, a generic USB resume implementation, or something more specialized? The name hints at a USB-related function dealing with resume events, likely after a suspend-to-RAM scenario.
I'm immediately looking for key aspects: What triggers this function? Is it a hardware interrupt, a kernel timer, or a specific USB event like a port change? I need to understand the entry point and the call chain to truly grasp its purpose. Next, I'm thinking about the resources it's manipulating. Is it touching USB controllers, individual devices, or power management registers? Any interaction with those controllers should be scrutinized to get the big picture.
I want to know how it handles error conditions. Does it have robust error checking and recovery mechanisms, or is it assuming everything will go smoothly? What kind of logging or debugging information does it provide? I need to see if it reports any failures, and if so, how.
From a debugging standpoint, I'll be looking for potential race conditions or synchronization issues. USB resume can be tricky with multiple devices competing for resources or different hardware components getting into an inconsistent state. Are there any locks or mutexes being used? I need to understand the locking strategy here.
Finally, I'm trying to determine the scope of this function. Does it handle the resume of a single USB device, or does it attempt to bring back the entire USB subsystem? Are there any device-specific quirks or workarounds implemented here? And ultimately, does it do its job effectively, and without any side-effects? This could be a critical point of failure in the system. Let's see how well it's written.
|
| 55/2 |
2026/05/16 14:10 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/input/misc/cm109.c IncludeLines:true Name:cm109_usb_resume]
Results: map[SourceCode: 824: static int cm109_usb_resume(struct usb_interface *intf)
825: {
826: struct cm109_dev *dev = usb_get_intfdata(intf);
827:
828: dev_info(&intf->dev, "cm109: usb_resume\n");
829:
830: guard(mutex)(&dev->pm_mutex);
831:
832: cm109_restore_state(dev);
833:
834: return 0;
835: }
SourceFile:drivers/input/misc/cm109.c]
|
| 56/2 |
2026/05/16 14:10 |
llm |
debugger |
0m
Model:
Tokens:
input: 121889
output: 49
thoughts: 23
Thoughts: Here's my attempt at summarizing the thought process, tailored for an expert audience:
**Analyzing the Resume Logic**
Okay, so `cm109_restore_state` is the function that actually handles the resumption process. That's good to know. Now, I need to dive into its implementation and understand exactly how it's orchestrating the restoration of the system to its previous state. I need to see if it's handling all of the relevant components – CPU registers, memory maps, peripheral states, everything. Is it using a consistent and robust approach? I'm particularly interested in how it's handling any potential race conditions during the restore. I'll need to look closely at its interactions with any underlying kernel mechanisms or hardware interfaces. It's time to dig into the code and see what's really happening under the hood. I expect a well-structured and documented approach if we're going to trust this function to handle system resumption reliably. I'm looking for elegance and correctness, but efficiency is crucial.
|
| 57/2 |
2026/05/16 14:10 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/input/misc/cm109.c IncludeLines:true Name:cm109_restore_state]
Results: map[SourceCode: 527: static void cm109_restore_state(struct cm109_dev *dev)
528: {
529: if (dev->open) {
530: /*
531: * Restore buzzer state.
532: * This will also kick regular URB submission
533: */
534: cm109_toggle_buzzer_async(dev);
535: }
536: }
SourceFile:drivers/input/misc/cm109.c]
|
| 58/2 |
2026/05/16 14:10 |
llm |
debugger |
0m
Model:
Tokens:
input: 122342
output: 43
thoughts: 7817
Thoughts: **Root Cause Analysis and Fix for Concurrent USB URB Submission in cm109 Driver**
Okay, so here's what I've got after digging through this cm109 driver code. The crash, the double add in `usb_submit_urb`, it all boils down to a race condition. Specifically, a very nasty one between `cm109_input_open` and `cm109_input_ev`. I'm seeing that `cm109_restore_state` and `cm109_usb_resume` are safe. The core issue is that `cm109_input_open` makes an unguarded call to `usb_submit_urb` to send an initialization command. And `cm109_input_ev` can also call `usb_submit_urb` through `cm109_toggle_buzzer_async`, but *it* *does* take a lock (`dev->ctl_submit_lock`). It's just that the calls can happen at the same time.
Here's the problem: `cm109_input_open` does its thing, sets `dev->ctl_urb_pending = 1`, and then calls `usb_submit_urb`. If `cm109_input_ev` fires at the same time or a tiny bit later (because of some interrupt or even just another CPU core), it will call `cm109_toggle_buzzer_async`. This *does* take the submit lock, but by then `dev->ctl_urb_pending` *might* have already been set. So both calls can potentially end up submitting the same URB at the same time, leading to that nasty double-add crash. It seems that the lock on `cm109_toggle_buzzer_async` is *not enough* to guard `cm109_input_open` from that state, which is very common.
My initial thought was to synchronize `cm109_input_open` with `dev->ctl_submit_lock`, but I think that is an incorrect solution because of modifying the `ctl_data`. I was right in thinking we can't change `dev->ctl_data` while an URB is pending.
So, the fix? Simple in concept. We need a guard flag on the event side. The solution is to add a check for the `dev->open` flag inside `cm109_input_ev`.
```c
case SND_TONE:
case SND_BELL:
dev->buzzer_state = !!value;
if (!dev->resetting && dev->open)
cm109_toggle_buzzer_async(dev);
return 0;
```
This ensures that `cm109_toggle_buzzer_async` is only called if the device is actually open. And `cm109_input_close()` needs to set `dev->open = 0` *before* it calls `cm109_stop_traffic` (to kill the URBs).
I had to be certain that all of the possible race conditions had been taken into consideration.
This approach stops `cm109_input_ev` from firing a URB if the device isn't fully initialized, solving the problem. The double URB submission will be history!
A little side note: I noticed that there were missing checks when `usb_submit_urb` fails to zero out `dev->ctl_urb_pending`, so I will want to fix that, but that is not related to the actual crash. I also need to make sure that the open flag is read safely, but it is just a plain integer, so that is not an issue.
|
| 59/2 |
2026/05/16 14:10 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/core/urb.c IncludeLines:true Name:usb_kill_urb]
Results: map[SourceCode: 676: /**
677: * usb_kill_urb - cancel a transfer request and wait for it to finish
678: * @urb: pointer to URB describing a previously submitted request,
679: * may be NULL
680: *
681: * This routine cancels an in-progress request. It is guaranteed that
682: * upon return all completion handlers will have finished and the URB
683: * will be totally idle and available for reuse. These features make
684: * this an ideal way to stop I/O in a disconnect() callback or close()
685: * function. If the request has not already finished or been unlinked
686: * the completion handler will see urb->status == -ENOENT.
687: *
688: * While the routine is running, attempts to resubmit the URB will fail
689: * with error -EPERM. Thus even if the URB's completion handler always
690: * tries to resubmit, it will not succeed and the URB will become idle.
691: *
692: * The URB must not be deallocated while this routine is running. In
693: * particular, when a driver calls this routine, it must insure that the
694: * completion handler cannot deallocate the URB.
695: *
696: * This routine may not be used in an interrupt context (such as a bottom
697: * half or a completion handler), or when holding a spinlock, or in other
698: * situations where the caller can't schedule().
699: *
700: * This routine should not be called by a driver after its disconnect
701: * method has returned.
702: */
703: void usb_kill_urb(struct urb *urb)
704: {
705: might_sleep();
706: if (!(urb && urb->dev && urb->ep))
707: return;
708: atomic_inc(&urb->reject);
709: /*
710: * Order the write of urb->reject above before the read
711: * of urb->use_count below. Pairs with the barriers in
712: * __usb_hcd_giveback_urb() and usb_hcd_submit_urb().
713: */
714: smp_mb__after_atomic();
715:
716: usb_hcd_unlink_urb(urb, -ENOENT);
717: wait_event(usb_kill_urb_queue, atomic_read(&urb->use_count) == 0);
718:
719: atomic_dec(&urb->reject);
720: }
SourceFile:drivers/usb/core/urb.c]
|
| 60/2 |
2026/05/16 14:10 |
llm |
debugger |
0m
Model:
Tokens:
input: 131799
output: 1359
thoughts: 3369
Thoughts: **Analysis of the CM109 USB Driver Race Condition and Proposed Fix**
Alright, let's break this down. It seems the core problem lies within the `cm109.c` driver, specifically a race condition involving the submission of `urb_ctl`. My thinking is this: we've got a scenario where `usb_submit_urb` can be called concurrently, leading to a "double add" within the USB stack. This occurs due to how `usb_submit_urb` handles the `urb->hcpriv` check which isn't protected by a proper lock.
The root of this issue stems from the interaction between `cm109_input_open` and `cm109_input_ev`. I see that `cm109_input_open` submits `dev->urb_ctl` without holding the `dev->ctl_submit_lock`. Meanwhile, `cm109_input_ev`, which I see is triggered by input events potentially *before* the device is fully opened, also calls `cm109_toggle_buzzer_async`, which *does* take the lock and then calls `usb_submit_urb` potentially even before the device has been opened.
Now, here's how the race condition manifests. Consider this sequence: `cm109_input_open` and `cm109_input_ev` are called at the same time. Because `cm109_input_open` submits the URB without a lock, it can call `usb_submit_urb(dev->urb_ctl)`. At the same time, `cm109_input_ev` starts to run, also calls `usb_submit_urb(dev->urb_ctl)`. Since the lock isn't held when submitting, the `urb->hcpriv` check in `usb_submit_urb` doesn't provide a perfect level of protection. Consequently, both threads will pass this check, which means the URB gets added twice to the list. That, is the double add.
To fix this, we need to carefully serialize our access. The key is in the lock and the `dev->open` status. I've landed on a few adjustments, specifically to `cm109_input_open`, `cm109_input_close`, and `cm109_toggle_buzzer_async`:
* **In `cm109_input_open`**: I'll ensure that `dev->ctl_submit_lock` is *held* when submitting the URB. This means setting `dev->open = 1` inside the lock as well. This will completely serialize the URB submission process with respect to other callers, such as the `cm109_input_ev` handler. It will also ensure that no URB is submitted until `dev->open` is set.
* **In `cm109_input_close`**: We'll also take the lock when setting `dev->open = 0`.
* **In `cm109_toggle_buzzer_async`**: I will add a check for `dev->open` under the protection of the lock before submitting the URB. This makes sure we do not submit the URB in `cm109_toggle_buzzer_async` when the device has been closed. If the device *is* open, we can proceed.
By making these changes, we've perfectly synchronized `dev->open` with the URB submission and will completely eliminate the double add bug!
|
| 61/1 |
2026/05/16 14:10 |
action |
kernel-scratch-checkouter |
0m
Results: map[KernelScratchSrc:/app/workdir/cache/tmp/tmp3640542038]
|
| 62/1 |
2026/05/16 14:10 |
loop |
|
47m
|
| 63/2 |
2026/05/16 14:10 |
iteration |
0 |
47m
|
| 64/3 |
2026/05/16 14:10 |
agent |
patch-generator |
0m
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.
Use the patch-diff tool to review the modifications you applied.
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.
Prefer calling several tools at the same time to save round-trips.
Prompt:
The crash that corresponds to the bug is:
list_add double add: new=ffff8880243bcb18, prev=ffff8880243bcb18, next=ffff888020f2e078.
------------[ cut here ]------------
kernel BUG at lib/list_debug.c:37!
Oops: invalid opcode: 0000 [#1] SMP KASAN PTI
CPU: 1 UID: 0 PID: 8662 Comm: syz.1.1154 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:__list_add_valid_or_report+0xa5/0x130 lib/list_debug.c:35
Code: 74 12 b0 01 5b 41 5c 41 5d 41 5e 41 5f 5d c3 cc cc cc cc cc 48 c7 c7 a0 e0 e7 8b 4c 89 fe 4c 89 f2 48 89 d9 e8 cc c2 6f fc 90 <0f> 0b 48 c7 c7 80 de e7 8b e8 bd c2 6f fc 90 0f 0b 48 c7 c7 40 df
RSP: 0018:ffffc90007cf7950 EFLAGS: 00010046
RAX: 0000000000000058 RBX: ffff888020f2e078 RCX: 07ffa03edd56da00
RDX: 0000000000000000 RSI: 0000000080000004 RDI: 0000000000000000
RBP: 1ffff110041e5c10 R08: ffffc90007cf76c7 R09: 1ffff92000f9eed8
R10: dffffc0000000000 R11: fffff52000f9eed9 R12: 1ffff11004877963
R13: dffffc0000000000 R14: ffff8880243bcb18 R15: ffff8880243bcb18
FS: 00007f62f8f336c0(0000) GS:ffff8880ebf64000(0000) knlGS:0000000000000000
CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 0000200000000180 CR3: 00000000280d4000 CR4: 00000000000006f0
Call Trace:
<TASK>
__list_add_valid include/linux/list.h:96 [inline]
__list_add include/linux/list.h:158 [inline]
list_add_tail include/linux/list.h:191 [inline]
usb_hcd_link_urb_to_ep+0x1d2/0x330 drivers/usb/core/hcd.c:1154
dummy_urb_enqueue+0x2a1/0x780 drivers/usb/gadget/udc/dummy_hcd.c:1292
usb_hcd_submit_urb+0x328/0x1b70 drivers/usb/core/hcd.c:1542
cm109_submit_buzz_toggle drivers/input/misc/cm109.c:351 [inline]
cm109_toggle_buzzer_async+0x114/0x250 drivers/input/misc/cm109.c:484
cm109_input_ev+0xed/0x1b0 drivers/input/misc/cm109.c:615
input_event_dispose+0x80/0x6b0 drivers/input/input.c:322
input_inject_event+0x1dc/0x330 drivers/input/input.c:424
kd_sound_helper+0x101/0x210 drivers/tty/vt/keyboard.c:257
input_handler_for_each_handle+0x101/0x1c0 drivers/input/input.c:2537
kd_mksound+0x96/0x130 drivers/tty/vt/keyboard.c:281
vt_k_ioctl drivers/tty/vt/vt_ioctl.c:303 [inline]
vt_ioctl+0x1c2d/0x20c0 drivers/tty/vt/vt_ioctl.c:743
tty_ioctl+0x928/0xde0 drivers/tty/tty_io.c:2792
vfs_ioctl fs/ioctl.c:51 [inline]
__do_sys_ioctl fs/ioctl.c:597 [inline]
__se_sys_ioctl+0xfc/0x170 fs/ioctl.c:583
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0x15f/0xf80 arch/x86/entry/syscall_64.c:94
entry_SYSCALL_64_after_hwframe+0x77/0x7f
RIP: 0033:0x7f62f7f9ce59
Code: ff c3 66 2e 0f 1f 84 00 00 00 00 00 0f 1f 44 00 00 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 c7 c1 e8 ff ff ff f7 d8 64 89 01 48
RSP: 002b:00007f62f8f33028 EFLAGS: 00000246 ORIG_RAX: 0000000000000010
RAX: ffffffffffffffda RBX: 00007f62f8215fa0 RCX: 00007f62f7f9ce59
RDX: 0000000000000000 RSI: 0000000000004b2f RDI: 0000000000000005
RBP: 00007f62f8032d6f R08: 0000000000000000 R09: 0000000000000000
R10: 0000000000000000 R11: 0000000000000246 R12: 0000000000000000
R13: 00007f62f8216038 R14: 00007f62f8215fa0 R15: 00007ffc9ebfae98
</TASK>
Modules linked in:
---[ end trace 0000000000000000 ]---
RIP: 0010:__list_add_valid_or_report+0xa5/0x130 lib/list_debug.c:35
Code: 74 12 b0 01 5b 41 5c 41 5d 41 5e 41 5f 5d c3 cc cc cc cc cc 48 c7 c7 a0 e0 e7 8b 4c 89 fe 4c 89 f2 48 89 d9 e8 cc c2 6f fc 90 <0f> 0b 48 c7 c7 80 de e7 8b e8 bd c2 6f fc 90 0f 0b 48 c7 c7 40 df
RSP: 0018:ffffc90007cf7950 EFLAGS: 00010046
RAX: 0000000000000058 RBX: ffff888020f2e078 RCX: 07ffa03edd56da00
RDX: 0000000000000000 RSI: 0000000080000004 RDI: 0000000000000000
RBP: 1ffff110041e5c10 R08: ffffc90007cf76c7 R09: 1ffff92000f9eed8
R10: dffffc0000000000 R11: fffff52000f9eed9 R12: 1ffff11004877963
R13: dffffc0000000000 R14: ffff8880243bcb18 R15: ffff8880243bcb18
FS: 00007f62f8f336c0(0000) GS:ffff8880ebf64000(0000) knlGS:0000000000000000
CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 0000200000000180 CR3: 00000000280d4000 CR4: 00000000000006f0
The explanation of the root cause of the bug is:
An analysis of the kernel crash reveals a race condition in the `cm109` input driver that leads to a double-add bug in the USB core's URB enqueueing mechanism.
### Root Cause Analysis
The crash `list_add double add` occurs in `usb_hcd_link_urb_to_ep()` when an URB is submitted while it is already on the endpoint's queue.
In `drivers/input/misc/cm109.c`, the control URB (`dev->urb_ctl`) is submitted in two main places:
1. **`cm109_input_open()`**: Called when the input device is opened. It submits the URB to initialize the device. Crucially, it does this **without** holding the `dev->ctl_submit_lock`.
2. **`cm109_input_ev()`**: The event handler for the input device. It can be triggered at any time by the input core (e.g., via `input_inject_event()` from `kd_mksound()` when a console beep is requested). When it receives a `SND_TONE` or `SND_BELL` event, it calls `cm109_toggle_buzzer_async()`, which acquires `dev->ctl_submit_lock` and submits the same `dev->urb_ctl`.
Because `cm109_input_ev()` does not check if the device is actually open (`dev->open`), and `cm109_input_open()` does not hold the `dev->ctl_submit_lock` when submitting the URB, these two functions can race and call `usb_submit_urb(dev->urb_ctl)` concurrently.
### Race Condition Details
While `usb_submit_urb()` has a safeguard check (`if (urb->hcpriv) return -EBUSY;`) to prevent submitting an already active URB, this check is **not protected by any lock**.
If `cm109_input_open()` and `cm109_input_ev()` call `usb_submit_urb()` at the exact same time:
1. Both threads read `urb->hcpriv` as `NULL`.
2. Both threads proceed to call `usb_hcd_submit_urb()`.
3. Both threads reach the HCD's `urb_enqueue` callback (e.g., `dummy_urb_enqueue()`).
4. The first thread successfully adds `urb->urb_list` to the endpoint's list (`urb->ep->urb_list`).
5. The second thread attempts to add the same `urb->urb_list` to the list, triggering the `list_add double add` BUG in `__list_add_valid()`.
### Proposed Fix
To fix this bug, the URB submission in `cm109_input_open()` must be serialized with `cm109_toggle_buzzer_async()`, and the event handler must not submit URBs if the device is closed.
1. **Serialize URB submission in `cm109_input_open()`**:
Acquire `dev->ctl_submit_lock` before submitting the URB. Because we are holding a spinlock, the memory allocation flag for `usb_submit_urb()` must be changed from `GFP_KERNEL` to `GFP_ATOMIC`.
2. **Check `dev->open` in `cm109_toggle_buzzer_async()`**:
Prevent the event handler from submitting URBs and restarting the polling loop when the device is closed.
3. **Safely update `dev->open`**:
Update the `dev->open` flag under the `dev->ctl_submit_lock` in both `cm109_input_open()` and `cm109_input_close()` to ensure perfect synchronization with the event handler.
Here is how the fix should look:
**In `cm109_input_open()`:**
```c
/* 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;
spin_lock_irq(&dev->ctl_submit_lock);
dev->open = 1;
dev->ctl_urb_pending = 1;
error = usb_submit_urb(dev->urb_ctl, GFP_ATOMIC); // Changed to GFP_ATOMIC
if (error) {
dev->ctl_urb_pending = 0;
dev->open = 0;
}
spin_unlock_irq(&dev->ctl_submit_lock);
if (!error)
return 0;
```
**In `cm109_input_close()`:**
```c
scoped_guard(mutex, &dev->pm_mutex) {
spin_lock_irq(&dev->ctl_submit_lock);
dev->open = 0;
spin_unlock_irq(&dev->ctl_submit_lock);
/* kill any pending urbs */
cm109_stop_traffic(dev);
}
```
**In `cm109_toggle_buzzer_async()`:**
```c
static void cm109_toggle_buzzer_async(struct cm109_dev *dev)
{
guard(spinlock_irqsave)(&dev->ctl_submit_lock);
if (!dev->open)
return;
if (dev->ctl_urb_pending) {
/* URB completion will resubmit */
dev->buzzer_pending = 1;
} else {
dev->ctl_urb_pending = 1;
cm109_submit_buzz_toggle(dev);
}
}
```
Reply: An analysis of the kernel crash reveals a race condition in the `cm109` input driver that leads to a double-add bug in the USB core's URB enqueueing mechanism.
### Root Cause Analysis
The crash `list_add double add` occurs in `usb_hcd_link_urb_to_ep()` when an URB is submitted while it is already on the endpoint's queue.
In `drivers/input/misc/cm109.c`, the control URB (`dev->urb_ctl`) is submitted in several places:
1. **`cm109_input_open()`**: Called when the input device is opened. It submits the URB to initialize the device without holding the `dev->ctl_submit_lock`.
2. **`cm109_toggle_buzzer_async()`**: The event handler for the input device. It can be triggered at any time by the input core (e.g., via `input_inject_event()` from `kd_mksound()` when a console beep is requested). It acquires `dev->ctl_submit_lock` and submits the same `dev->urb_ctl`.
3. **`cm109_submit_ctl()`**: Called from the completion handler of `dev->urb_irq`. It acquires `dev->ctl_submit_lock` and submits `dev->urb_ctl`.
There are two distinct race conditions that can lead to a double submit:
1. **Race between `cm109_input_open()` and `cm109_toggle_buzzer_async()`**: Because `cm109_input_ev()` does not check if the device is actually open (`dev->open`), and `cm109_input_open()` does not hold the `dev->ctl_submit_lock` when submitting the URB, these two functions can race and call `usb_submit_urb(dev->urb_ctl)` concurrently.
2. **Race between `cm109_submit_ctl()` and `cm109_toggle_buzzer_async()`**: If `cm109_toggle_buzzer_async()` is called while `dev->urb_irq` is in flight, it sees `dev->ctl_urb_pending == 0`, sets it to 1, and submits `dev->urb_ctl`. When `dev->urb_irq` completes, it calls `cm109_submit_ctl()`. However, `cm109_submit_ctl()` does not check `dev->ctl_urb_pending` before submitting `dev->urb_ctl`, leading to a double submit if the URB is already in flight.
### Fix
To fix these bugs, we need to properly serialize URB submissions and state changes using `dev->ctl_submit_lock`:
1. **Serialize URB submission in `cm109_input_open()`**:
Acquire `dev->ctl_submit_lock` before submitting the URB. Because we are holding a spinlock, the memory allocation flag for `usb_submit_urb()` must be changed from `GFP_KERNEL` to `GFP_ATOMIC`.
2. **Check `dev->open` in `cm109_toggle_buzzer_async()`**:
Prevent the event handler from submitting URBs and restarting the polling loop when the device is closed.
3. **Safely update `dev->open`**:
Update the `dev->open` flag under the `dev->ctl_submit_lock` in both `cm109_input_open()` and `cm109_input_close()` to ensure perfect synchronization with the event handler.
4. **Check `dev->ctl_urb_pending` in `cm109_submit_ctl()`**:
If `dev->ctl_urb_pending` is already 1, `cm109_submit_ctl()` must return immediately to prevent a double submit and to avoid modifying the transfer buffer (`dev->ctl_data`) while the URB is in flight.
I have applied these changes to `drivers/input/misc/cm109.c` using the `codeeditor` tool.
|
| 65/4 |
2026/05/16 14:10 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 8182
output: 27
thoughts: 267
Thoughts: **Analyzing the CM109 USB Driver Race Condition**
Okay, so we've got a nasty bug report: a double-add issue in `usb_hcd_link_urb_to_ep`, specifically within the `usb_hcd_link_urb_to_ep` function. That's a red flag immediately. Digging deeper, it looks like the root cause is a race condition in `drivers/input/misc/cm109.c`. Seems like the `dev->urb_ctl` is getting submitted concurrently from two different places: `cm109_input_open()` and the event handler, specifically `cm109_input_ev()` which is called, through the `cm109_toggle_buzzer_async()` function. This is not ideal! It's a classic concurrency problem.
The proposed solution seems sensible and addresses the core issue. First, the fix is to serialize URB submissions in `cm109_input_open()` by using `dev->ctl_submit_lock`. That's a good approach to ensure exclusive access to the USB resource. Next, they're changing `GFP_KERNEL` to `GFP_ATOMIC` in `cm109_input_open()` when calling `usb_submit_urb()`. Considering the open context, this is a reasonable adjustment to ensure that no blocking operation could interfare the submit process. Following that, to make sure this doesn't happen again, checking `dev->open` in `cm109_toggle_buzzer_async()` to prevent the event handler from submitting URBs when the device is closed is a smart move. Finally, updating `dev->open` under `dev->ctl_submit_lock` in both `cm109_input_open()` and `cm109_input_close()` ensures that the open/close state is consistent with the submission lock.
I'll dive into `drivers/input/misc/cm109.c` and take a closer look at the code to ensure the proposed solution is robust and covers all potential race conditions. Seems like a textbook example of concurrent access issues. This is something I'm used to seeing in these types of drivers.
|
| 66/4 |
2026/05/16 14:10 |
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");
]
|
| 67/4 |
2026/05/16 14:10 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 18897
output: 435
thoughts: 1037
Thoughts: **Analyzing and Refining the CM109 Driver's Initialization**
Okay, let's break this down. I'm looking at the `cm109` driver's input open function, specifically `cm109_input_open()`, and comparing it to a proposed fix. The goal seems to be improving how the device is initialized upon opening, specifically in relation to the control URB and the buzzer.
First, I observe that the original `cm109_input_open()` function retrieves the interface and attempts an autoresume. Then, it initializes several device members: `buzzer_state`, `key_code`, and `keybit`. This makes sense – setting the initial state of the hardware. The next part, however, is where the proposed change kicks in.
In the original code, the `ctl_urb_pending` flag is set *before* the `usb_submit_urb()` call and is not handled if the usb submission fails. If the submission fails, the `dev->open` flag is never set to 1. This means the device will never work if usb submission fails in the input open.
The proposed fix, as I see it, focuses on synchronizing the URB submission process and the driver's open state by employing a spinlock (`ctl_submit_lock`) and a scoped guard (using a mutex `pm_mutex`). This ensures that only one thread can submit the URB at a time, protecting against race conditions. It also properly handles error conditions within the critical section.
So, the new logic starts by acquiring the mutex. Inside the protected block, it sets the initial device state. It then sets up the control data (using `HID_OR_GPO_BUZ_SPDIF` and `dev->keybit`) and *then* tries to submit the URB. The `ctl_urb_pending` flag is set and the `open` flag is set *only* if `usb_submit_urb()` succeeds. If it fails, the open flag is never set.
Upon successful URB submission, the function *returns* 0. If there's an error during the URB submission, the code cleans up by setting `ctl_urb_pending` to 0 and, critically, releases the interface before returning the error code.
The complementary parts, `cm109_input_close()` and `cm109_toggle_buzzer_async()`, appear to be correctly integrated with this change. `cm109_input_close()` now locks the `ctl_submit_lock` to ensure the `open` flag is properly set to 0. It also calls `cm109_stop_traffic()` to ensure that no more traffic is sent to the device. Finally, `cm109_toggle_buzzer_async()` uses the `ctl_submit_lock` to check if `open` is set and also determines if the URB is pending.
The improved logic appears to be:
1. Ensuring exclusive access using the `pm_mutex` for power management and the `ctl_submit_lock` for URB submission and driver state.
2. Properly handles USB submission failure on initialization.
3. Synchronizing the `open` state with URB submission.
This seems like a solid, well-structured fix that should provide increased robustness and reliability in handling the CM109 device's initialization and buzzer control, so I will apply the proposed change.
|
| 68/4 |
2026/05/16 14:10 |
tool |
codeeditor |
0m
Args: map[CurrentCode: /* 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;
NewCode: /* 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;
spin_lock_irq(&dev->ctl_submit_lock);
dev->open = 1;
dev->ctl_urb_pending = 1;
error = usb_submit_urb(dev->urb_ctl, GFP_ATOMIC);
if (error) {
dev->ctl_urb_pending = 0;
dev->open = 0;
}
spin_unlock_irq(&dev->ctl_submit_lock);
if (!error)
return 0;
}
SourceFile:drivers/input/misc/cm109.c]
Results:
|
| 69/4 |
2026/05/16 14:10 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 21120
output: 227
thoughts: 154
Thoughts: Okay, let's break this down.
**Closing the CM109 Input Device**
Right, so I'm looking at the `cm109_input_close()` function, which is responsible for shutting down the CM109 input device. First, I need to get the device-specific data from the `input_dev` structure. That's straightforward using `input_get_drvdata()`.
Now, I'm setting up a `scoped_guard` with `mutex` and `dev->pm_mutex`. This is a crucial section because I need to ensure mutual exclusion while handling the device shutdown. This scoped guard guarantees that the mutex is always properly released, even if errors occur during execution.
Within the critical section protected by the mutex, I know that event delivery has been halted, so I don't need to worry about any race conditions involving the buzzer starting again after I start the stop-traffic sequence. The next step is to call `cm109_stop_traffic(dev)`. This handles stopping any ongoing data transfer or device activity.
Then, I need to mark the device as closed by setting `dev->open = 0`. This is the final step within the mutex-protected block.
Finally, I'm using `usb_autopm_put_interface(dev->intf)` to decrement the USB AutoPM reference count for the interface. This will allow the USB subsystem to power manage the interface as needed. So, this last call is about managing power states and resource cleanup with the USB interface. That covers the entire close sequence.
|
| 70/4 |
2026/05/16 14:10 |
tool |
codeeditor |
0m
Args: map[CurrentCode: 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;
}
NewCode: scoped_guard(mutex, &dev->pm_mutex) {
spin_lock_irq(&dev->ctl_submit_lock);
dev->open = 0;
spin_unlock_irq(&dev->ctl_submit_lock);
/*
* Once we are here event delivery is stopped so we
* don't need to worry about someone starting buzzer
* again
*/
cm109_stop_traffic(dev);
}
SourceFile:drivers/input/misc/cm109.c]
Results:
|
| 71/4 |
2026/05/16 14:10 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 21845
output: 287
thoughts: 138
Thoughts: Okay, here's my interpretation of this snippet, thinking through the control flow like I'm working on it myself.
**Buzzer Toggle Handling - Asynchronous Approach**
Alright, I'm looking at `cm109_toggle_buzzer_async()`, which handles the asynchronous toggling of the buzzer on the CM109 device. It's designed to be non-blocking, so I need to make sure I don't get hung up here.
First, I see the `guard(spinlock_irqsave)(&dev->ctl_submit_lock)`. This is standard, protecting the critical section with a spinlock and disabling interrupts on the current CPU, to ensure atomicity around the control submission. Got it – thread safety is paramount.
Now comes the crucial part: `if (dev->ctl_urb_pending)`. I need to figure out the status of the USB request block (`URB`) used for communication. If `ctl_urb_pending` is already set, an asynchronous transfer is already in progress. In this scenario, I *don't* submit a new URB right away. Instead, I set `dev->buzzer_pending = 1`. This flag acts as a signal to the completion handler that a buzzer toggle was requested while another one was in flight. The completion handler will take care of the subsequent toggle, preventing race conditions and potential issues with multiple concurrent requests.
However, if `ctl_urb_pending` is *not* set, meaning there's no ongoing transfer, I get to proceed with submitting the request. I set `dev->ctl_urb_pending = 1` to indicate that a URB has been submitted and that an asynchronous transfer is in progress. Then, I call `cm109_submit_buzz_toggle(dev)`. This will be responsible for building and submitting the URB to the USB stack. It would include details like which endpoint to send to, the data to send (in this case, I'd assume it's a command to toggle the buzzer), and any necessary flags. This is likely where the actual hardware interaction happens.
In short, it's a careful dance: check for pending URBs, queue if necessary, and only submit if the coast is clear, ensuring a reliable toggle operation without blocking. This approach keeps the driver responsive.
|
| 72/4 |
2026/05/16 14:10 |
tool |
codeeditor |
0m
Args: map[CurrentCode: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);
}
}
NewCode:static void cm109_toggle_buzzer_async(struct cm109_dev *dev)
{
guard(spinlock_irqsave)(&dev->ctl_submit_lock);
if (!dev->open)
return;
if (dev->ctl_urb_pending) {
/* URB completion will resubmit */
dev->buzzer_pending = 1;
} else {
dev->ctl_urb_pending = 1;
cm109_submit_buzz_toggle(dev);
}
}
SourceFile:drivers/input/misc/cm109.c]
Results:
|
| 73/4 |
2026/05/16 14:10 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 22772
output: 27
thoughts: 8
Thoughts: Okay, let's dive into this. Here's my thought process as I approach the provided task, assuming I'm an expert in my field and presented as a fulsome summary:
**Initial Assessment: Navigating the Patch Diff**
Right, so we've got a patch diff to review. My immediate instinct is to get a handle on the scope of the changes. I'm already mentally forming questions: *What's the overall context? Is this a bug fix, a feature addition, a refactor, or something else entirely? What modules or areas of the codebase are impacted? What's the potential risk profile associated with these changes, considering our current architectural state and known vulnerabilities?*
My first step is to quickly scan the diff itself, probably starting with the file headers and then the most substantial hunks. I'm looking for a "lay of the land" view, noting any files that jump out at me as particularly critical or potentially problematic based on my prior knowledge of the system. I will look for patterns, as my mind is forming the big picture. Are there recurring changes? Are there changes touching core components? Are there areas I'm unfamiliar with?
Next, I need to evaluate the details. I will quickly compare with the current version and think about why each change was made and if the changes make sense. I'll need to assess the technical merit of each change. Does the logic hold? Are there obvious edge cases overlooked? Are there potential performance bottlenecks introduced? Does the code adhere to our established coding style and best practices? I need to analyze for any security vulnerabilities.
I'll be paying close attention to any changes in interfaces or data structures, as these have the potential to ripple throughout the codebase. The goal is to perform a sanity check to make sure the core design remains consistent with what's expected and intended.
Ultimately, I'm aiming for a comprehensive assessment, identifying both potential positives and negatives of the patch. I need to be ready to articulate my findings clearly, and support them with solid reasoning. This will allow for more focused discussions with the development team and other stakeholders.
|
| 74/4 |
2026/05/16 14:10 |
tool |
patch-diff |
0m
Args: map[File:drivers/input/misc/cm109.c]
Results: map[Output:diff --git a/drivers/input/misc/cm109.c b/drivers/input/misc/cm109.c
index 353d3c1d3..687d02fe1 100644
--- a/drivers/input/misc/cm109.c
+++ b/drivers/input/misc/cm109.c
@@ -469,20 +469,23 @@ static void cm109_urb_ctl_callback(struct urb *urb)
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->open)
+ return;
+
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)
@@ -542,62 +545,69 @@ static void cm109_restore_state(struct cm109_dev *dev)
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;
+ spin_lock_irq(&dev->ctl_submit_lock);
+ dev->open = 1;
dev->ctl_urb_pending = 1;
- error = usb_submit_urb(dev->urb_ctl, GFP_KERNEL);
- if (!error) {
- dev->open = 1;
- return 0;
+ error = usb_submit_urb(dev->urb_ctl, GFP_ATOMIC);
+ if (error) {
+ dev->ctl_urb_pending = 0;
+ dev->open = 0;
}
- }
+ spin_unlock_irq(&dev->ctl_submit_lock);
- dev->ctl_urb_pending = 0;
+ if (!error)
+ return 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) {
+ spin_lock_irq(&dev->ctl_submit_lock);
+ dev->open = 0;
+ spin_unlock_irq(&dev->ctl_submit_lock);
+
/*
* 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);
]
|
| 75/4 |
2026/05/16 14:10 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 24481
output: 325
thoughts: 9436
Thoughts: **Double Submit Bug Analysis and Fix**
Okay, so I've gone over this code pretty thoroughly. The initial patch seemed sound, but after digging in, it looks like there's a race condition leading to a potential double submission of the `urb_ctl` URB.
Here's the breakdown: The core problem stems from how `dev->ctl_urb_pending` is handled and the interplay between `cm109_input_open`, `cm109_toggle_buzzer_async`, `cm109_submit_ctl`, and the URB callback functions. There are two identified double-submit bugs. The first is in `cm109_input_open` and `cm109_toggle_buzzer_async`. The second is in `cm109_submit_ctl` and `cm109_toggle_buzzer_async`.
* **First Double Submit Bug:** The race condition happens if `cm109_toggle_buzzer_async` runs concurrently with `cm109_input_open`, which doesn't protect the URB submission with the spinlock. `cm109_toggle_buzzer_async` can then attempt to submit `urb_ctl` *before* `cm109_input_open` sets `dev->ctl_urb_pending = 1` within `ctl_submit_lock`. The fix is to add `spin_lock_irq(&dev->ctl_submit_lock);` around `dev->ctl_urb_pending = 1;` in `cm109_input_open`.
* **Second Double Submit Bug:** The other race condition occurs when `cm109_toggle_buzzer_async` triggers `urb_ctl` submission while `urb_irq` is in flight. After a buzzer event, the keyboard data and buzzer bit are sent via `urb_ctl` which then triggers `urb_irq`. The cycle goes `urb_ctl` -> `urb_irq` -> `urb_ctl` -> etc. If `cm109_toggle_buzzer_async` is called while `urb_irq` is pending, the following sequence happens: `urb_ctl` completes, submits `urb_irq`; `cm109_toggle_buzzer_async` triggers `urb_ctl`; `urb_irq` completes, calls `cm109_submit_ctl`. The `cm109_submit_ctl` function is called and since it does not check the `dev->ctl_urb_pending` status of the `urb_ctl` URB, then the URB is submitted twice. This double submission can lead to a crash.
The solution is to add a check for `dev->ctl_urb_pending` within `cm109_submit_ctl`: `if (dev->ctl_urb_pending) return;`. If another submission of `urb_ctl` is pending, do nothing and return.
A side note: If `cm109_submit_ctl` returns because `dev->ctl_urb_pending` is 1, the `keybit` information gets delayed by one cycle, but it doesn't cause any critical issues because `urb_ctl` will be submitted on the subsequent event.
|
| 76/4 |
2026/05/16 14:10 |
tool |
codeeditor |
0m
Args: map[CurrentCode: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;
NewCode: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->ctl_urb_pending)
return;
if (dev->buzzer_state)
dev->ctl_data->byte[HID_OR0] |= BUZZER_ON;
else
dev->ctl_data->byte[HID_OR0] &= ~BUZZER_ON;
SourceFile:drivers/input/misc/cm109.c]
Results:
|
| 77/4 |
2026/05/16 14:10 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 34964
output: 27
thoughts: 2156
Thoughts: **Analysis of URB Submission Logic in cm109 Driver**
Okay, let's break this down. My initial concern was about the potential for race conditions and double submissions of URBs, specifically with `urb_ctl` and `urb_irq`. The central issue here seems to be ensuring that the HID report data, particularly the `keybit` and buzzer state, are properly updated and transmitted in a consistent, alternating sequence.
First, I examined the scenario where `cm109_submit_ctl` returns because `dev->ctl_urb_pending` is already set. In this case, `urb_ctl` is *not* submitted with the new `keybit`. Instead, the submission of `urb_irq` is deferred until `urb_ctl` *does* complete. The callback `cm109_urb_ctl_callback` then correctly handles submitting `urb_irq` if `buzzer_pending` is not set. The next `urb_irq` will then call `cm109_submit_ctl` to submit the updated data, including the latest `keybit`. This means that data is submitted, alternating, in the following sequence:
`urb_irq` (completes) -> `cm109_submit_ctl` (returns because `urb_ctl` is pending)
`urb_ctl` (completes) -> `cm109_urb_ctl_callback` -> submits `urb_irq`
`urb_irq` (completes) -> `cm109_submit_ctl` -> submits `urb_ctl`
So the alternation is maintained, even if `cm109_submit_ctl` is called repeatedly.
Next, I considered what happens if the `urb_ctl` completes *before* the `urb_irq`. In this situation, `cm109_urb_ctl_callback` checks `dev->irq_urb_pending`. If it's already set to 1 (because `cm109_submit_ctl` had returned earlier), it does nothing. So, nothing happens when `urb_ctl` completes. But, when `urb_irq` eventually completes, the new data is then set via the next call to `cm109_submit_ctl`. So the two cases are handled correctly to ensure there are no double submits, and the data is properly updated in the correct order.
I then revisited the entirety of `cm109_submit_ctl`. The `dev->buzzer_pending` flag clearing is not a problem. Even when the function returns because `ctl_urb_pending` is already set, `buzzer_pending` is left unchanged. This is correct as `cm109_toggle_buzzer_async` may have set it. Its value is only used by the callback to resubmit `urb_ctl` later.
I reviewed `cm109_urb_ctl_callback` again, to confirm error handling. If an error occurs during the URB transfer (status != 0), the function resubmits `urb_ctl` via `cm109_submit_buzz_toggle`. In this situation, submitting `urb_irq` is intentionally skipped, which is good. This ensures that the error condition is handled by resubmitting `urb_ctl` until it succeeds. The next successful completion will trigger the submission of `urb_irq`.
The `cm109_input_open` is also correct. `cm109_input_open` starts the alternating submission correctly, so the system is correctly set up.
**Conclusion:**
The logic appears sound. The mechanism of using the `irq_urb_pending` and `ctl_urb_pending` flags, and the callback function correctly ensures that `urb_ctl` and `urb_irq` are submitted in the correct sequence, handling various scenarios and avoiding double submissions. The error handling for the `urb_ctl` transfer is also correct. The updates I made are correct as they maintain the existing state machine.
|
| 78/4 |
2026/05/16 14:10 |
tool |
patch-diff |
0m
Args: map[File:drivers/input/misc/cm109.c]
Results: map[Output:diff --git a/drivers/input/misc/cm109.c b/drivers/input/misc/cm109.c
index 353d3c1d3..4479233d0 100644
--- a/drivers/input/misc/cm109.c
+++ b/drivers/input/misc/cm109.c
@@ -358,32 +358,35 @@ 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;
if (unlikely(dev->shutdown))
return;
+ if (dev->ctl_urb_pending)
+ 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
*/
@@ -469,20 +472,23 @@ static void cm109_urb_ctl_callback(struct urb *urb)
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->open)
+ return;
+
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)
@@ -542,62 +548,69 @@ static void cm109_restore_state(struct cm109_dev *dev)
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;
+ spin_lock_irq(&dev->ctl_submit_lock);
+ dev->open = 1;
dev->ctl_urb_pending = 1;
- error = usb_submit_urb(dev->urb_ctl, GFP_KERNEL);
- if (!error) {
- dev->open = 1;
- return 0;
+ error = usb_submit_urb(dev->urb_ctl, GFP_ATOMIC);
+ if (error) {
+ dev->ctl_urb_pending = 0;
+ dev->open = 0;
}
- }
+ spin_unlock_irq(&dev->ctl_submit_lock);
- dev->ctl_urb_pending = 0;
+ if (!error)
+ return 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) {
+ spin_lock_irq(&dev->ctl_submit_lock);
+ dev->open = 0;
+ spin_unlock_irq(&dev->ctl_submit_lock);
+
/*
* 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);
]
|
| 79/4 |
2026/05/16 14:10 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 39631
output: 914
thoughts: 4500
Thoughts: **Final Assessment of the USB Driver Patch**
Alright, let's break this down. My initial assessment was that the patch looks sound and addresses the core race condition. It focuses on several key areas:
1. **Serialization of URB Submissions:** The code now correctly uses the `dev->ctl_submit_lock` to protect the submission of `urb_ctl` in `cm109_input_open`. This is critical. The lock ensures that only one thread can submit the URB at a time, preventing concurrent submissions from, for instance, `cm109_toggle_buzzer_async`. It's also setting `dev->open` under the lock.
2. **Device Open State:** The addition of `dev->open` and its proper handling in `cm109_input_open` and `cm109_input_close` is spot on. `cm109_toggle_buzzer_async` now checks `dev->open` under the same lock *before* trying to submit any URBs. This elegantly solves the problem of trying to send commands to a closed device.
3. **Preventing Double Submits:** `cm109_submit_ctl` now includes a check for `dev->ctl_urb_pending`. This stops the code from resubmitting the `urb_ctl` if it's already in flight, which could occur due to various async interactions, particularly with the buzzer toggle and interrupt handling.
4. **Data Integrity:** I extensively reviewed all locations where `dev->ctl_data` (the transfer buffer) is modified. The code ensures data modification *only* happens when the URB is not pending. This is absolutely essential to prevent data corruption.
5. **Synchronization of Buzzer Toggle:** The buzzer toggle logic (`cm109_toggle_buzzer_async` and `cm109_submit_buzz_toggle`) has been examined. The asynchronous logic has been checked to ensure that the proper state and control are set under the `ctl_submit_lock`. The use of `dev->buzzer_pending` and `dev->ctl_urb_pending` works to ensure that buzzer toggles do not interfere with other actions and vice versa.
I did a deep dive into places where the `urb_ctl` is submitted or where `dev->ctl_data` is modified. I've re-examined the code multiple times for race conditions. I am extremely confident that the modifications do not introduce race conditions or corrupt data. The initial assessment was correct; the code is complete and correctly handles all the potential issues. I am now certain this fix is optimal. The use of `GFP_ATOMIC` is correct. The error handling of `usb_submit_urb` in `cm109_input_open` is consistent and now safe under the lock.
|
| 80/3 |
2026/05/16 14:10 |
action |
test-patch |
47m
Results: map[PatchDiff:diff --git a/drivers/input/misc/cm109.c b/drivers/input/misc/cm109.c
index 353d3c1d3..4479233d0 100644
--- a/drivers/input/misc/cm109.c
+++ b/drivers/input/misc/cm109.c
@@ -366,6 +366,9 @@ static void cm109_submit_ctl(struct cm109_dev *dev)
if (unlikely(dev->shutdown))
return;
+ if (dev->ctl_urb_pending)
+ return;
+
if (dev->buzzer_state)
dev->ctl_data->byte[HID_OR0] |= BUZZER_ON;
else
@@ -476,6 +479,9 @@ static void cm109_toggle_buzzer_async(struct cm109_dev *dev)
{
guard(spinlock_irqsave)(&dev->ctl_submit_lock);
+ if (!dev->open)
+ return;
+
if (dev->ctl_urb_pending) {
/* URB completion will resubmit */
dev->buzzer_pending = 1;
@@ -562,15 +568,19 @@ static int cm109_input_open(struct input_dev *idev)
dev->ctl_data->byte[HID_OR2] = dev->keybit;
dev->ctl_data->byte[HID_OR3] = 0x00;
+ spin_lock_irq(&dev->ctl_submit_lock);
+ dev->open = 1;
dev->ctl_urb_pending = 1;
- error = usb_submit_urb(dev->urb_ctl, GFP_KERNEL);
- if (!error) {
- dev->open = 1;
- return 0;
+ error = usb_submit_urb(dev->urb_ctl, GFP_ATOMIC);
+ if (error) {
+ dev->ctl_urb_pending = 0;
+ dev->open = 0;
}
- }
+ spin_unlock_irq(&dev->ctl_submit_lock);
- dev->ctl_urb_pending = 0;
+ if (!error)
+ return 0;
+ }
usb_autopm_put_interface(dev->intf);
dev_err(&dev->intf->dev, "%s: usb_submit_urb (urb_ctl) failed %d\n",
@@ -584,13 +594,16 @@ static void cm109_input_close(struct input_dev *idev)
struct cm109_dev *dev = input_get_drvdata(idev);
scoped_guard(mutex, &dev->pm_mutex) {
+ spin_lock_irq(&dev->ctl_submit_lock);
+ dev->open = 0;
+ spin_unlock_irq(&dev->ctl_submit_lock);
+
/*
* 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);
TestError:]
|
| 81/1 |
2026/05/16 14:57 |
agent |
fixes-finder |
4m
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:
list_add double add: new=ffff8880243bcb18, prev=ffff8880243bcb18, next=ffff888020f2e078.
------------[ cut here ]------------
kernel BUG at lib/list_debug.c:37!
Oops: invalid opcode: 0000 [#1] SMP KASAN PTI
CPU: 1 UID: 0 PID: 8662 Comm: syz.1.1154 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:__list_add_valid_or_report+0xa5/0x130 lib/list_debug.c:35
Code: 74 12 b0 01 5b 41 5c 41 5d 41 5e 41 5f 5d c3 cc cc cc cc cc 48 c7 c7 a0 e0 e7 8b 4c 89 fe 4c 89 f2 48 89 d9 e8 cc c2 6f fc 90 <0f> 0b 48 c7 c7 80 de e7 8b e8 bd c2 6f fc 90 0f 0b 48 c7 c7 40 df
RSP: 0018:ffffc90007cf7950 EFLAGS: 00010046
RAX: 0000000000000058 RBX: ffff888020f2e078 RCX: 07ffa03edd56da00
RDX: 0000000000000000 RSI: 0000000080000004 RDI: 0000000000000000
RBP: 1ffff110041e5c10 R08: ffffc90007cf76c7 R09: 1ffff92000f9eed8
R10: dffffc0000000000 R11: fffff52000f9eed9 R12: 1ffff11004877963
R13: dffffc0000000000 R14: ffff8880243bcb18 R15: ffff8880243bcb18
FS: 00007f62f8f336c0(0000) GS:ffff8880ebf64000(0000) knlGS:0000000000000000
CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 0000200000000180 CR3: 00000000280d4000 CR4: 00000000000006f0
Call Trace:
<TASK>
__list_add_valid include/linux/list.h:96 [inline]
__list_add include/linux/list.h:158 [inline]
list_add_tail include/linux/list.h:191 [inline]
usb_hcd_link_urb_to_ep+0x1d2/0x330 drivers/usb/core/hcd.c:1154
dummy_urb_enqueue+0x2a1/0x780 drivers/usb/gadget/udc/dummy_hcd.c:1292
usb_hcd_submit_urb+0x328/0x1b70 drivers/usb/core/hcd.c:1542
cm109_submit_buzz_toggle drivers/input/misc/cm109.c:351 [inline]
cm109_toggle_buzzer_async+0x114/0x250 drivers/input/misc/cm109.c:484
cm109_input_ev+0xed/0x1b0 drivers/input/misc/cm109.c:615
input_event_dispose+0x80/0x6b0 drivers/input/input.c:322
input_inject_event+0x1dc/0x330 drivers/input/input.c:424
kd_sound_helper+0x101/0x210 drivers/tty/vt/keyboard.c:257
input_handler_for_each_handle+0x101/0x1c0 drivers/input/input.c:2537
kd_mksound+0x96/0x130 drivers/tty/vt/keyboard.c:281
vt_k_ioctl drivers/tty/vt/vt_ioctl.c:303 [inline]
vt_ioctl+0x1c2d/0x20c0 drivers/tty/vt/vt_ioctl.c:743
tty_ioctl+0x928/0xde0 drivers/tty/tty_io.c:2792
vfs_ioctl fs/ioctl.c:51 [inline]
__do_sys_ioctl fs/ioctl.c:597 [inline]
__se_sys_ioctl+0xfc/0x170 fs/ioctl.c:583
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0x15f/0xf80 arch/x86/entry/syscall_64.c:94
entry_SYSCALL_64_after_hwframe+0x77/0x7f
RIP: 0033:0x7f62f7f9ce59
Code: ff c3 66 2e 0f 1f 84 00 00 00 00 00 0f 1f 44 00 00 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 c7 c1 e8 ff ff ff f7 d8 64 89 01 48
RSP: 002b:00007f62f8f33028 EFLAGS: 00000246 ORIG_RAX: 0000000000000010
RAX: ffffffffffffffda RBX: 00007f62f8215fa0 RCX: 00007f62f7f9ce59
RDX: 0000000000000000 RSI: 0000000000004b2f RDI: 0000000000000005
RBP: 00007f62f8032d6f R08: 0000000000000000 R09: 0000000000000000
R10: 0000000000000000 R11: 0000000000000246 R12: 0000000000000000
R13: 00007f62f8216038 R14: 00007f62f8215fa0 R15: 00007ffc9ebfae98
</TASK>
Modules linked in:
---[ end trace 0000000000000000 ]---
RIP: 0010:__list_add_valid_or_report+0xa5/0x130 lib/list_debug.c:35
Code: 74 12 b0 01 5b 41 5c 41 5d 41 5e 41 5f 5d c3 cc cc cc cc cc 48 c7 c7 a0 e0 e7 8b 4c 89 fe 4c 89 f2 48 89 d9 e8 cc c2 6f fc 90 <0f> 0b 48 c7 c7 80 de e7 8b e8 bd c2 6f fc 90 0f 0b 48 c7 c7 40 df
RSP: 0018:ffffc90007cf7950 EFLAGS: 00010046
RAX: 0000000000000058 RBX: ffff888020f2e078 RCX: 07ffa03edd56da00
RDX: 0000000000000000 RSI: 0000000080000004 RDI: 0000000000000000
RBP: 1ffff110041e5c10 R08: ffffc90007cf76c7 R09: 1ffff92000f9eed8
R10: dffffc0000000000 R11: fffff52000f9eed9 R12: 1ffff11004877963
R13: dffffc0000000000 R14: ffff8880243bcb18 R15: ffff8880243bcb18
FS: 00007f62f8f336c0(0000) GS:ffff8880ebf64000(0000) knlGS:0000000000000000
CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 0000200000000180 CR3: 00000000280d4000 CR4: 00000000000006f0
The explanation of the root cause is:
An analysis of the kernel crash reveals a race condition in the `cm109` input driver that leads to a double-add bug in the USB core's URB enqueueing mechanism.
### Root Cause Analysis
The crash `list_add double add` occurs in `usb_hcd_link_urb_to_ep()` when an URB is submitted while it is already on the endpoint's queue.
In `drivers/input/misc/cm109.c`, the control URB (`dev->urb_ctl`) is submitted in two main places:
1. **`cm109_input_open()`**: Called when the input device is opened. It submits the URB to initialize the device. Crucially, it does this **without** holding the `dev->ctl_submit_lock`.
2. **`cm109_input_ev()`**: The event handler for the input device. It can be triggered at any time by the input core (e.g., via `input_inject_event()` from `kd_mksound()` when a console beep is requested). When it receives a `SND_TONE` or `SND_BELL` event, it calls `cm109_toggle_buzzer_async()`, which acquires `dev->ctl_submit_lock` and submits the same `dev->urb_ctl`.
Because `cm109_input_ev()` does not check if the device is actually open (`dev->open`), and `cm109_input_open()` does not hold the `dev->ctl_submit_lock` when submitting the URB, these two functions can race and call `usb_submit_urb(dev->urb_ctl)` concurrently.
### Race Condition Details
While `usb_submit_urb()` has a safeguard check (`if (urb->hcpriv) return -EBUSY;`) to prevent submitting an already active URB, this check is **not protected by any lock**.
If `cm109_input_open()` and `cm109_input_ev()` call `usb_submit_urb()` at the exact same time:
1. Both threads read `urb->hcpriv` as `NULL`.
2. Both threads proceed to call `usb_hcd_submit_urb()`.
3. Both threads reach the HCD's `urb_enqueue` callback (e.g., `dummy_urb_enqueue()`).
4. The first thread successfully adds `urb->urb_list` to the endpoint's list (`urb->ep->urb_list`).
5. The second thread attempts to add the same `urb->urb_list` to the list, triggering the `list_add double add` BUG in `__list_add_valid()`.
### Proposed Fix
To fix this bug, the URB submission in `cm109_input_open()` must be serialized with `cm109_toggle_buzzer_async()`, and the event handler must not submit URBs if the device is closed.
1. **Serialize URB submission in `cm109_input_open()`**:
Acquire `dev->ctl_submit_lock` before submitting the URB. Because we are holding a spinlock, the memory allocation flag for `usb_submit_urb()` must be changed from `GFP_KERNEL` to `GFP_ATOMIC`.
2. **Check `dev->open` in `cm109_toggle_buzzer_async()`**:
Prevent the event handler from submitting URBs and restarting the polling loop when the device is closed.
3. **Safely update `dev->open`**:
Update the `dev->open` flag under the `dev->ctl_submit_lock` in both `cm109_input_open()` and `cm109_input_close()` to ensure perfect synchronization with the event handler.
Here is how the fix should look:
**In `cm109_input_open()`:**
```c
/* 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;
spin_lock_irq(&dev->ctl_submit_lock);
dev->open = 1;
dev->ctl_urb_pending = 1;
error = usb_submit_urb(dev->urb_ctl, GFP_ATOMIC); // Changed to GFP_ATOMIC
if (error) {
dev->ctl_urb_pending = 0;
dev->open = 0;
}
spin_unlock_irq(&dev->ctl_submit_lock);
if (!error)
return 0;
```
**In `cm109_input_close()`:**
```c
scoped_guard(mutex, &dev->pm_mutex) {
spin_lock_irq(&dev->ctl_submit_lock);
dev->open = 0;
spin_unlock_irq(&dev->ctl_submit_lock);
/* kill any pending urbs */
cm109_stop_traffic(dev);
}
```
**In `cm109_toggle_buzzer_async()`:**
```c
static void cm109_toggle_buzzer_async(struct cm109_dev *dev)
{
guard(spinlock_irqsave)(&dev->ctl_submit_lock);
if (!dev->open)
return;
if (dev->ctl_urb_pending) {
/* URB completion will resubmit */
dev->buzzer_pending = 1;
} else {
dev->ctl_urb_pending = 1;
cm109_submit_buzz_toggle(dev);
}
}
```
The patch that fixes the bug is:
diff --git a/drivers/input/misc/cm109.c b/drivers/input/misc/cm109.c
index 353d3c1d3..4479233d0 100644
--- a/drivers/input/misc/cm109.c
+++ b/drivers/input/misc/cm109.c
@@ -366,6 +366,9 @@ static void cm109_submit_ctl(struct cm109_dev *dev)
if (unlikely(dev->shutdown))
return;
+ if (dev->ctl_urb_pending)
+ return;
+
if (dev->buzzer_state)
dev->ctl_data->byte[HID_OR0] |= BUZZER_ON;
else
@@ -476,6 +479,9 @@ static void cm109_toggle_buzzer_async(struct cm109_dev *dev)
{
guard(spinlock_irqsave)(&dev->ctl_submit_lock);
+ if (!dev->open)
+ return;
+
if (dev->ctl_urb_pending) {
/* URB completion will resubmit */
dev->buzzer_pending = 1;
@@ -562,15 +568,19 @@ static int cm109_input_open(struct input_dev *idev)
dev->ctl_data->byte[HID_OR2] = dev->keybit;
dev->ctl_data->byte[HID_OR3] = 0x00;
+ spin_lock_irq(&dev->ctl_submit_lock);
+ dev->open = 1;
dev->ctl_urb_pending = 1;
- error = usb_submit_urb(dev->urb_ctl, GFP_KERNEL);
- if (!error) {
- dev->open = 1;
- return 0;
+ error = usb_submit_urb(dev->urb_ctl, GFP_ATOMIC);
+ if (error) {
+ dev->ctl_urb_pending = 0;
+ dev->open = 0;
}
- }
+ spin_unlock_irq(&dev->ctl_submit_lock);
- dev->ctl_urb_pending = 0;
+ if (!error)
+ return 0;
+ }
usb_autopm_put_interface(dev->intf);
dev_err(&dev->intf->dev, "%s: usb_submit_urb (urb_ctl) failed %d\n",
@@ -584,13 +594,16 @@ static void cm109_input_close(struct input_dev *idev)
struct cm109_dev *dev = input_get_drvdata(idev);
scoped_guard(mutex, &dev->pm_mutex) {
+ spin_lock_irq(&dev->ctl_submit_lock);
+ dev->open = 0;
+ spin_unlock_irq(&dev->ctl_submit_lock);
+
/*
* 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);
Search for the commit(s) that introduced this bug.
|
| 82/2 |
2026/05/16 14:57 |
llm |
fixes-finder |
0m
Model:
Tokens:
input: 8744
output: 28
thoughts: 204
Thoughts: **Pinpointing the Culprit in cm109.c**
Alright, let's get down to brass tacks. We've got a bug, a nasty race condition, brewing in the `cm109.c` driver, specifically between `cm109_input_open()` and that buzzing function, `cm109_toggle_buzzer_async()`. My gut tells me this stems from the way they're accessing and modifying shared state.
The key to understanding this is the patch introduced: it looks like they added `dev->open = 1` within a spinlock in `cm109_input_open()`, presumably to track whether the device is open. Then, over in `cm109_toggle_buzzer_async()`, there's a check for `!dev->open`. That’s my first red flag: it's likely a flag to control what happens if the device isn't ready. And I see a safety check in `cm109_submit_ctl()` which has a check if `dev->ctl_urb_pending`, which probably ties into the asychronous handling.
So, the game plan is clear. I need to dive into the commit history of `drivers/input/misc/cm109.c`, focusing like a laser on the evolution of `cm109_input_open()` and `cm109_toggle_buzzer_async()`. I'll be looking for any changes that introduced that `dev->open` flag and the subsequent conditional logic. I need to see *when* and *how* those methods were modified to trigger this race. The devil is in the details, so let's start digging.
|
| 83/2 |
2026/05/16 14:58 |
tool |
git-log |
0m
Args: map[PathPrefix:drivers/input/misc/cm109.c]
Results: map[Output:bf4afc53b77a Convert 'alloc_obj' family to use the new default GFP_KERNEL argument
69050f8d6d07 treewide: Replace kmalloc with kmalloc_obj for non-scalar types
d8a43a83633a Input: cm109 - use guard notation when acquiring mutex and spinlock
948bf187694f Input: remove third argument of usb_maxpacket()
82e060904732 Input: cm109 - do not stomp on control URB
08a6caaed8c1 Input: cm109 - remove needless check before usb_free_coherent()
a10e763b8713 treewide: Replace GPLv2 boilerplate/reference with SPDX - rule 372
ac2ee9ba953a Input: cm109 - validate number of endpoints before using them
d144cf42eee0 Input: cm109 - spin_lock in complete() cleanup
c90a0f08fedc Input: cm109 - fix handling of volume and mute buttons
]
|
| 84/2 |
2026/05/16 14:58 |
llm |
fixes-finder |
0m
Model:
Tokens:
input: 9628
output: 27
thoughts: 34
Thoughts: Okay, let's break this down.
**Analyzing Commit 82e060904732: Input, cm109, and Avoiding Control URB Stomping**
Right, I'm looking at commit `82e060904732`. The context is clearly input handling, specifically dealing with a system component identified as "cm109". The key phrase here is "do not stomp on control URB". That immediately tells me this is likely related to USB communication, probably low-level device interaction. A "URB" – a USB Request Block – is the fundamental unit of data transfer in USB. "Control" implies a specific type of URB: one used for configuration, status queries, and general command-and-control operations with the device, and not for simple data transfers.
So, the issue is that this commit is *preventing* something bad from happening. It's not a new feature, but a *fix* for a potential bug. My immediate interpretation is that some existing code was overwriting, corrupting, or otherwise interfering with a control URB. This could lead to a variety of problems: device malfunction, incorrect device configuration, data corruption, and potentially even system instability, because it's messing with the low level USB protocol. The commit, therefore, *must* be preventing the program from writing over the existing commands.*
What this means in practical terms, depends a lot on the surrounding code, and I'd need to dig deeper into the code of cm109 to understand exactly the nature of the "stomping." But the intent is clear: protect the control URB from unauthorized modifications or from something that is interfering with it. I'd be looking for race conditions, incorrect memory management, or possibly a flawed design in the existing input handling logic. This commit is likely a crucial step towards robust and reliable USB device interactions in this system.
|
| 85/2 |
2026/05/16 14:58 |
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);
]
|
| 86/2 |
2026/05/16 14:58 |
llm |
fixes-finder |
0m
Model:
Tokens:
input: 10668
output: 63
thoughts: 114
Thoughts: **Analyzing a Potential Race Condition in the CM109 Driver**
Okay, so I'm digging into this reported bug with the CM109 driver, specifically dealing with a race condition between `cm109_input_open()` and `cm109_toggle_buzzer_async()`. The initial clue, this commit `82e060904732`, introduces `dev->ctl_urb_pending = 1;` within `cm109_input_open()` *before* the `usb_submit_urb(dev->urb_ctl, GFP_KERNEL)` call. That immediately flagged my attention. I need to understand the timing implications of that line, right? Setting that flag *before* the URB submission. Now, I should shift my focus to `cm109_toggle_buzzer_async()` to see how it interacts with the control URB. Let's trace that function's history and see what we can find. I'm guessing that the presence of the flag might cause the issue.
|
| 87/2 |
2026/05/16 14:58 |
tool |
git-log |
0m
Args: map[CodeRegexp:cm109_toggle_buzzer_async SourcePath:drivers/input/misc/cm109.c SymbolName:cm109_toggle_buzzer_async]
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
]
|
| 88/2 |
2026/05/16 14:58 |
llm |
fixes-finder |
0m
Model:
Tokens:
input: 11142
output: 27
thoughts: 23
Thoughts: Okay, here's my attempt at summarizing that thought process, keeping in mind I'm speaking to a fellow expert:
**Tracing Down the Driver Addition**
Alright, so I need to understand what this commit, `c04148f915e5`, did in relation to this driver. My initial instinct is to use the commit hash directly to jump into the details. I'm thinking I'll start with a `git show` or `git log -p` to get a good diff and see the exact files that were modified. From there, I can probably piece together the nature of the changes, any relevant context, and the overall impact of this commit on the system, specifically concerning the driver. This is standard operating procedure for me - trace the change, understand the rationale, and identify any potential implications for the current code.
|
| 89/2 |
2026/05/16 14:59 |
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");
]
|
| 90/2 |
2026/05/16 14:59 |
llm |
fixes-finder |
0m
Model:
Tokens:
input: 22254
output: 57
thoughts: 405
Thoughts: **Analyzing a Race Condition in the cm109 Driver**
Okay, so I've pinpointed a tricky bug – a race condition between `cm109_input_open()` and `cm109_input_ev()`. It seems that `cm109_input_ev()` can trigger `cm109_toggle_buzzer_async()`, which in turn calls `cm109_submit_buzz_toggle()`. Meanwhile, `cm109_input_open()` initiates `usb_submit_urb()` with `dev->urb_ctl`. The issue is, `cm109_submit_buzz_toggle()` also calls `usb_submit_urb()` using `GFP_ATOMIC`, and this creates a potential conflict.
I'm thinking this likely stems from the initial commit introducing the driver, `c04148f915e5ba7947752e6348e0da4cdab1329e`. Let me confirm that `cm109_input_ev()` *could* be called before or concurrently with `cm109_input_open()`. Yes, `input_inject_event()` is the culprit; it can be called at any time, even before a user-space process has opened the device. The driver registers the input device during `cm109_usb_probe()`, and events can be injected right after `input_register_device()`. So, the race has been present since the beginning, as suspected.
Hold on a minute, I'm now looking at the proposed fix which includes `if (dev->ctl_urb_pending) return;` in `cm109_submit_ctl()`. But wasn't that `cm109_submit_buzz_toggle()` previously? Let's clarify when `cm109_submit_ctl()` was introduced or when the renaming happened.
|
| 91/2 |
2026/05/16 14:59 |
tool |
git-log |
0m
Args: map[CodeRegexp:cm109_submit_ctl SourcePath:drivers/input/misc/cm109.c SymbolName:cm109_submit_ctl]
Results: map[Output:d8a43a83633a Input: cm109 - use guard notation when acquiring mutex and spinlock
]
|
| 92/2 |
2026/05/16 14:59 |
llm |
fixes-finder |
0m
Model:
Tokens:
input: 23177
output: 27
thoughts: 18
Thoughts: Okay, here's my interpretation of that.
**Analyzing the Hash: `d8a43a83633a`**
Alright, let's break this down. The first thing that jumps out at me is this identifier: `d8a43a83633a`. Immediately, my mind starts racing, as any expert would. Given the format, it looks like a potential hexadecimal hash. It's a string of characters that is 12 characters long. The length alone gives me a few initial clues, but I need to know a few things to really understand its meaning. I'd begin by making a few initial assessments.
1. **Context is Key:** First I need context, such as what system this hash is related to. If this is from a database, a code repository, a filesystem, or a network log, it can inform me of the likely algorithms or technologies that were used to generate it.
2. **Algorithm Guesses:** Depending on that context, I'd quickly start thinking about potential hashing algorithms. This length is suggestive of a few possibilities. Given its length, there's a strong chance it's some sort of shortened or modified hash or a part of a larger hash. It's too short for MD5, SHA-1, or SHA-256 in their standard forms. It *could* be a truncated SHA-1 or a similar algorithm, a custom hash function, or something like a MurmurHash or CityHash variation.
3. **Quick Checks:** I'd probably run this through a few common hash identifier tools. Even if the tool doesn't identify it definitively, the information it returns could provide a much narrower range of possibilities. I will also check any known databases of common hashes.
4. **Database Lookup:** At a glance, I don't see anything immediately obvious. I would run this through our internal database for quick reference as well.
Ultimately, without further context, I am left with several potential paths to investigate. Without more information, I can only provide an initial assessment and suggested next steps to arrive at a better understanding.
|
| 93/2 |
2026/05/16 15:00 |
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;
}
]
|
| 94/2 |
2026/05/16 15:00 |
llm |
fixes-finder |
0m
Model:
Tokens:
input: 27046
output: 51
thoughts: 850
Thoughts: **Analyzing the cm109 Driver Race Condition**
Okay, so `cm109_submit_ctl()` got a crucial modification in `d8a43a83633a4770f00ee77079fbb7a2218cea2a` – a check for `dev->ctl_urb_pending`. This seems relevant, and the bug fix is clearly related to this function. My initial thought is that this check was added to address a race. The core of this issue revolves around a race condition between `cm109_input_open()` and `cm109_toggle_buzzer_async()`.
Looking closer, `cm109_toggle_buzzer_async()` checks `dev->ctl_urb_pending` and sets `dev->buzzer_pending` if it's already pending. The key here is that `cm109_input_open()` sets `dev->ctl_urb_pending = 1` *before* submitting the URB. Now, here's the kicker: `cm109_input_open()` doesn't hold `dev->ctl_submit_lock` when setting `dev->ctl_urb_pending` or calling `usb_submit_urb()`, while `cm109_toggle_buzzer_async()` *does* hold that lock.
This means that `cm109_toggle_buzzer_async()` could read `dev->ctl_urb_pending` as 0 if it runs before `cm109_input_open()` sets it. Or if it runs after but there's no memory barrier. Or, even worse, if it runs before `cm109_input_open()` is even called.
The scenario that's really screaming out is this: If `cm109_input_ev()` is called *before* `cm109_input_open()`, `dev->ctl_urb_pending` is going to be 0. `cm109_toggle_buzzer_async()` then sets `dev->ctl_urb_pending = 1` and calls `cm109_submit_buzz_toggle()` (which in turn calls `usb_submit_urb()`). Then, `cm109_input_open()` is subsequently called. It sets `dev->ctl_urb_pending = 1` (even if it's already 1, it rewrites the value), and also calls `usb_submit_urb()`. Boom! Double submission! That's the problem.
Essentially, the race is between `cm109_input_ev()` and `cm109_input_open()`. `cm109_input_ev()` can run concurrently with, or even *before*, `cm109_input_open()`.
Now, the concerning thing is that this vulnerability has likely been present since the driver was initially added in `c04148f915e5ba7947752e6348e0da4cdab1329e`. Wait a minute... In the original code, `cm109_input_open()` *didn't* set `dev->ctl_urb_pending = 1`! I'm going to have to review that original commit again.
|
| 95/2 |
2026/05/16 15:00 |
tool |
git-show |
0m
Args: map[Commit:c04148f915e5ba7947752e6348e0da4cdab1329e]
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");
]
|
| 96/2 |
2026/05/16 15:00 |
llm |
fixes-finder |
0m
Model:
Tokens:
input: 39564
output: 57
thoughts: 4728
Thoughts: **Analysis of the cm109 Driver's Race Condition**
Okay, so I've been digging into this cm109 driver, and the race condition is pretty clear now. It boils down to a classic case of concurrent access to a shared resource without proper synchronization. Looking at the code, specifically the sequence of `cm109_input_open()`, `cm109_input_ev()`, `cm109_toggle_buzzer_async()`, and `cm109_submit_buzz_toggle()`, the core problem is that `usb_submit_urb()` can be called concurrently from different contexts.
Initially, `cm109_input_open()` submits a control URB to initialize the device without holding `dev->ctl_submit_lock`. At the same time, `cm109_input_ev()` can be called, which, based on input events like buzzer triggers, leads to a call to `cm109_toggle_buzzer_async()`. This function *does* use the lock, but only when it is submitting the URB. The initial vulnerability is that `cm109_input_open()` opens the door to `usb_submit_urb()`'s concurrent calling without synchronization.
The crux of the matter is the `dev->ctl_urb_pending` flag, which was added in a later commit (`82e06090473289ce63e23fdeb8737aad59b10645`). This flag was *meant* to prevent double submissions of the URB, which is a big issue, but it didn't solve the race condition because `cm109_input_open()` and `cm109_input_ev()` never used the lock at the same time. The intent was correct, but the execution was flawed because it didn't actually synchronize the access to the URB. This means that a race could still happen: if `cm109_input_open()` hadn't finished its `usb_submit_urb()` call and set the flag before `cm109_toggle_buzzer_async()` or if it was interrupted by another thread calling `cm109_input_ev()`, then we have the concurrent calls again.
Let me clarify it further; after the "fix" in `82e06090473289ce63e23fdeb8737aad59b10645`, the `dev->ctl_urb_pending` flag's functionality could work in two scenarios. Suppose that before this commit, `cm109_input_open()` would call `usb_submit_urb()` which submits the control URB without the flag being set. Then after this command, even before the URB completes, `cm109_input_ev()` would call `cm109_toggle_buzzer_async()`. Since `cm109_toggle_buzzer_async()` would check the `ctl_urb_pending` flag, it could submit the URB again which leads to a double-add issue. The second scenario after the commit happens when the "fix" is used where this double add issue could be fixed.
Therefore, although the intent was a fix in `82e06090473289ce63e23fdeb8737aad59b10645`, the original source of the race lies in the initial implementation of the driver (`c04148f915e5ba7947752e6348e0da4cdab1329e`) because `cm109_input_open()` doesn't hold the lock when submitting the URB and also because `cm109_input_ev()` doesn't check if the device is actually open. Without proper locking or state checks, any concurrent execution from the open and event handling path can lead to issues.
|
| 97/2 |
2026/05/16 15:01 |
tool |
git-log |
0m
Args: map[CodeRegexp:cm109_input_ev SourcePath:drivers/input/misc/cm109.c SymbolName:cm109_input_ev]
Results: map[Output:ab242a73a109 USB: input: cm109.c: fix up dev_* messages
2d517cab0107 Input: cm109 - add missing newlines to messages
67d47641b5d2 Input: cm109 - don't use obsolete logging macros
c04148f915e5 Input: add driver for USB VoIP phones with CM109 chipset
]
|
| 98/2 |
2026/05/16 15:01 |
llm |
fixes-finder |
0m
Model:
Tokens:
input: 45346
output: 57
thoughts: 87
Thoughts: Here's my thought process, as if I were analyzing this code:
**Analyzing the `cm109_input_ev` Function**
Okay, so the `cm109_input_ev` function was introduced in that specific commit, `c04148f915e5ba7947752e6348e0da4cdab1329e`. Good to know the origin. And the critical point is that its interaction with `dev->open` hasn't been altered since then. That's key context to hold in mind. It tells me that any bugs related to that specific part of the code are unlikely to stem from recent changes within `cm109_input_ev`. I'm starting to think about where to look for potential issues. To dig further, I want to see how `cm109_input_open` is used. Therefore, I will now use the git log to examine commits that interact with `cm109_input_open`.
|
| 99/2 |
2026/05/16 15:01 |
tool |
git-log |
0m
Args: map[CodeRegexp:cm109_input_open SourcePath:drivers/input/misc/cm109.c SymbolName:cm109_input_open]
Results: map[Output:d8a43a83633a Input: cm109 - use guard notation when acquiring mutex and spinlock
82e060904732 Input: cm109 - do not stomp on control URB
ab242a73a109 USB: input: cm109.c: fix up dev_* messages
3f37d8e8e18f USB: cm109.c: remove err() usage
c04148f915e5 Input: add driver for USB VoIP phones with CM109 chipset
]
|
| 100/2 |
2026/05/16 15:02 |
llm |
fixes-finder |
0m
Model:
Tokens:
input: 45864
output: 53
thoughts: 1047
Thoughts: **Bug Root Cause Analysis: cm109 Driver Double URB Submission**
Okay, so looking at this, the issue stems from the `cm109_input_open` function in the driver. It looks like the bug was introduced with the commit `c04148f915e5ba7947752e6348e0da4cdab1329e`. That commit is the culprit. Specifically, the function `cm109_input_open` calls `usb_submit_urb()` but it only holds `dev->pm_mutex` at that time. It misses holding `dev->ctl_submit_lock` during that crucial `usb_submit_urb()` call.
Taking a closer look at the code, in that same commit, `cm109_input_open()` doesn't initialize `dev->ctl_urb_pending` either. That is the key! This missing initialization, coupled with the lack of lock protection, leads to a race condition. If `cm109_input_ev()` is triggered *after* `cm109_input_open()` has submitted the URB, `cm109_toggle_buzzer_async()` sees `dev->ctl_urb_pending == 0` and, therefore, submits the URB again. The double submission is guaranteed if `cm109_input_ev()` gets called while the URB is still active. Concurrent calls between `cm109_input_ev()` and `cm109_input_open()` can also cause the double submit.
The commit `82e06090473289ce63e23fdeb8737aad59b10645` attempted to address the issue by setting `dev->ctl_urb_pending = 1` in `cm109_input_open()`, but it failed because it still didn't use the lock, so the concurrent race condition was still present. It also missed checking `dev->open` in `cm109_input_ev()`, thus, could still lead to a double submission even if it's called prior to `cm109_input_open()`.
Therefore, the root cause is the lack of proper synchronization and state checking introduced back with the initial driver commit. I'm going to set that original commit `c04148f915e5ba7947752e6348e0da4cdab1329e` as the root cause.
|
| 101/2 |
2026/05/16 15:02 |
tool |
set-results |
0m
Args: map[FixesHash:c04148f915e5ba7947752e6348e0da4cdab1329e]
Results: map[FixesHash:c04148f915e5ba7947752e6348e0da4cdab1329e]
|
| 102/1 |
2026/05/16 15:02 |
action |
format-fixes |
0m
Results: map[Fixes:map[Hash:c04148f915e5ba7947752e6348e0da4cdab1329e Title:Input: add driver for USB VoIP phones with CM109 chipset]]
|
| 103/1 |
2026/05/16 15:02 |
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]]]
|
| 104/1 |
2026/05/16 15:02 |
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
]
|
| 105/1 |
2026/05/16 15:02 |
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.
IMPORTANT: Do not wrap lines manually (e.g., at 80 characters); we will reformat the text
automatically, so keep paragraphs as single lines without newlines.
Prefer calling several tools at the same time to save round-trips.
Prompt:
The crash that corresponds to the bug is:
list_add double add: new=ffff8880243bcb18, prev=ffff8880243bcb18, next=ffff888020f2e078.
------------[ cut here ]------------
kernel BUG at lib/list_debug.c:37!
Oops: invalid opcode: 0000 [#1] SMP KASAN PTI
CPU: 1 UID: 0 PID: 8662 Comm: syz.1.1154 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:__list_add_valid_or_report+0xa5/0x130 lib/list_debug.c:35
Code: 74 12 b0 01 5b 41 5c 41 5d 41 5e 41 5f 5d c3 cc cc cc cc cc 48 c7 c7 a0 e0 e7 8b 4c 89 fe 4c 89 f2 48 89 d9 e8 cc c2 6f fc 90 <0f> 0b 48 c7 c7 80 de e7 8b e8 bd c2 6f fc 90 0f 0b 48 c7 c7 40 df
RSP: 0018:ffffc90007cf7950 EFLAGS: 00010046
RAX: 0000000000000058 RBX: ffff888020f2e078 RCX: 07ffa03edd56da00
RDX: 0000000000000000 RSI: 0000000080000004 RDI: 0000000000000000
RBP: 1ffff110041e5c10 R08: ffffc90007cf76c7 R09: 1ffff92000f9eed8
R10: dffffc0000000000 R11: fffff52000f9eed9 R12: 1ffff11004877963
R13: dffffc0000000000 R14: ffff8880243bcb18 R15: ffff8880243bcb18
FS: 00007f62f8f336c0(0000) GS:ffff8880ebf64000(0000) knlGS:0000000000000000
CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 0000200000000180 CR3: 00000000280d4000 CR4: 00000000000006f0
Call Trace:
<TASK>
__list_add_valid include/linux/list.h:96 [inline]
__list_add include/linux/list.h:158 [inline]
list_add_tail include/linux/list.h:191 [inline]
usb_hcd_link_urb_to_ep+0x1d2/0x330 drivers/usb/core/hcd.c:1154
dummy_urb_enqueue+0x2a1/0x780 drivers/usb/gadget/udc/dummy_hcd.c:1292
usb_hcd_submit_urb+0x328/0x1b70 drivers/usb/core/hcd.c:1542
cm109_submit_buzz_toggle drivers/input/misc/cm109.c:351 [inline]
cm109_toggle_buzzer_async+0x114/0x250 drivers/input/misc/cm109.c:484
cm109_input_ev+0xed/0x1b0 drivers/input/misc/cm109.c:615
input_event_dispose+0x80/0x6b0 drivers/input/input.c:322
input_inject_event+0x1dc/0x330 drivers/input/input.c:424
kd_sound_helper+0x101/0x210 drivers/tty/vt/keyboard.c:257
input_handler_for_each_handle+0x101/0x1c0 drivers/input/input.c:2537
kd_mksound+0x96/0x130 drivers/tty/vt/keyboard.c:281
vt_k_ioctl drivers/tty/vt/vt_ioctl.c:303 [inline]
vt_ioctl+0x1c2d/0x20c0 drivers/tty/vt/vt_ioctl.c:743
tty_ioctl+0x928/0xde0 drivers/tty/tty_io.c:2792
vfs_ioctl fs/ioctl.c:51 [inline]
__do_sys_ioctl fs/ioctl.c:597 [inline]
__se_sys_ioctl+0xfc/0x170 fs/ioctl.c:583
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0x15f/0xf80 arch/x86/entry/syscall_64.c:94
entry_SYSCALL_64_after_hwframe+0x77/0x7f
RIP: 0033:0x7f62f7f9ce59
Code: ff c3 66 2e 0f 1f 84 00 00 00 00 00 0f 1f 44 00 00 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 c7 c1 e8 ff ff ff f7 d8 64 89 01 48
RSP: 002b:00007f62f8f33028 EFLAGS: 00000246 ORIG_RAX: 0000000000000010
RAX: ffffffffffffffda RBX: 00007f62f8215fa0 RCX: 00007f62f7f9ce59
RDX: 0000000000000000 RSI: 0000000000004b2f RDI: 0000000000000005
RBP: 00007f62f8032d6f R08: 0000000000000000 R09: 0000000000000000
R10: 0000000000000000 R11: 0000000000000246 R12: 0000000000000000
R13: 00007f62f8216038 R14: 00007f62f8215fa0 R15: 00007ffc9ebfae98
</TASK>
Modules linked in:
---[ end trace 0000000000000000 ]---
RIP: 0010:__list_add_valid_or_report+0xa5/0x130 lib/list_debug.c:35
Code: 74 12 b0 01 5b 41 5c 41 5d 41 5e 41 5f 5d c3 cc cc cc cc cc 48 c7 c7 a0 e0 e7 8b 4c 89 fe 4c 89 f2 48 89 d9 e8 cc c2 6f fc 90 <0f> 0b 48 c7 c7 80 de e7 8b e8 bd c2 6f fc 90 0f 0b 48 c7 c7 40 df
RSP: 0018:ffffc90007cf7950 EFLAGS: 00010046
RAX: 0000000000000058 RBX: ffff888020f2e078 RCX: 07ffa03edd56da00
RDX: 0000000000000000 RSI: 0000000080000004 RDI: 0000000000000000
RBP: 1ffff110041e5c10 R08: ffffc90007cf76c7 R09: 1ffff92000f9eed8
R10: dffffc0000000000 R11: fffff52000f9eed9 R12: 1ffff11004877963
R13: dffffc0000000000 R14: ffff8880243bcb18 R15: ffff8880243bcb18
FS: 00007f62f8f336c0(0000) GS:ffff8880ebf64000(0000) knlGS:0000000000000000
CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 0000200000000180 CR3: 00000000280d4000 CR4: 00000000000006f0
The explanation of the root cause of the bug is:
An analysis of the kernel crash reveals a race condition in the `cm109` input driver that leads to a double-add bug in the USB core's URB enqueueing mechanism.
### Root Cause Analysis
The crash `list_add double add` occurs in `usb_hcd_link_urb_to_ep()` when an URB is submitted while it is already on the endpoint's queue.
In `drivers/input/misc/cm109.c`, the control URB (`dev->urb_ctl`) is submitted in two main places:
1. **`cm109_input_open()`**: Called when the input device is opened. It submits the URB to initialize the device. Crucially, it does this **without** holding the `dev->ctl_submit_lock`.
2. **`cm109_input_ev()`**: The event handler for the input device. It can be triggered at any time by the input core (e.g., via `input_inject_event()` from `kd_mksound()` when a console beep is requested). When it receives a `SND_TONE` or `SND_BELL` event, it calls `cm109_toggle_buzzer_async()`, which acquires `dev->ctl_submit_lock` and submits the same `dev->urb_ctl`.
Because `cm109_input_ev()` does not check if the device is actually open (`dev->open`), and `cm109_input_open()` does not hold the `dev->ctl_submit_lock` when submitting the URB, these two functions can race and call `usb_submit_urb(dev->urb_ctl)` concurrently.
### Race Condition Details
While `usb_submit_urb()` has a safeguard check (`if (urb->hcpriv) return -EBUSY;`) to prevent submitting an already active URB, this check is **not protected by any lock**.
If `cm109_input_open()` and `cm109_input_ev()` call `usb_submit_urb()` at the exact same time:
1. Both threads read `urb->hcpriv` as `NULL`.
2. Both threads proceed to call `usb_hcd_submit_urb()`.
3. Both threads reach the HCD's `urb_enqueue` callback (e.g., `dummy_urb_enqueue()`).
4. The first thread successfully adds `urb->urb_list` to the endpoint's list (`urb->ep->urb_list`).
5. The second thread attempts to add the same `urb->urb_list` to the list, triggering the `list_add double add` BUG in `__list_add_valid()`.
### Proposed Fix
To fix this bug, the URB submission in `cm109_input_open()` must be serialized with `cm109_toggle_buzzer_async()`, and the event handler must not submit URBs if the device is closed.
1. **Serialize URB submission in `cm109_input_open()`**:
Acquire `dev->ctl_submit_lock` before submitting the URB. Because we are holding a spinlock, the memory allocation flag for `usb_submit_urb()` must be changed from `GFP_KERNEL` to `GFP_ATOMIC`.
2. **Check `dev->open` in `cm109_toggle_buzzer_async()`**:
Prevent the event handler from submitting URBs and restarting the polling loop when the device is closed.
3. **Safely update `dev->open`**:
Update the `dev->open` flag under the `dev->ctl_submit_lock` in both `cm109_input_open()` and `cm109_input_close()` to ensure perfect synchronization with the event handler.
Here is how the fix should look:
**In `cm109_input_open()`:**
```c
/* 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;
spin_lock_irq(&dev->ctl_submit_lock);
dev->open = 1;
dev->ctl_urb_pending = 1;
error = usb_submit_urb(dev->urb_ctl, GFP_ATOMIC); // Changed to GFP_ATOMIC
if (error) {
dev->ctl_urb_pending = 0;
dev->open = 0;
}
spin_unlock_irq(&dev->ctl_submit_lock);
if (!error)
return 0;
```
**In `cm109_input_close()`:**
```c
scoped_guard(mutex, &dev->pm_mutex) {
spin_lock_irq(&dev->ctl_submit_lock);
dev->open = 0;
spin_unlock_irq(&dev->ctl_submit_lock);
/* kill any pending urbs */
cm109_stop_traffic(dev);
}
```
**In `cm109_toggle_buzzer_async()`:**
```c
static void cm109_toggle_buzzer_async(struct cm109_dev *dev)
{
guard(spinlock_irqsave)(&dev->ctl_submit_lock);
if (!dev->open)
return;
if (dev->ctl_urb_pending) {
/* URB completion will resubmit */
dev->buzzer_pending = 1;
} else {
dev->ctl_urb_pending = 1;
cm109_submit_buzz_toggle(dev);
}
}
```
The diff of the bug fix is:
diff --git a/drivers/input/misc/cm109.c b/drivers/input/misc/cm109.c
index 353d3c1d3..4479233d0 100644
--- a/drivers/input/misc/cm109.c
+++ b/drivers/input/misc/cm109.c
@@ -366,6 +366,9 @@ static void cm109_submit_ctl(struct cm109_dev *dev)
if (unlikely(dev->shutdown))
return;
+ if (dev->ctl_urb_pending)
+ return;
+
if (dev->buzzer_state)
dev->ctl_data->byte[HID_OR0] |= BUZZER_ON;
else
@@ -476,6 +479,9 @@ static void cm109_toggle_buzzer_async(struct cm109_dev *dev)
{
guard(spinlock_irqsave)(&dev->ctl_submit_lock);
+ if (!dev->open)
+ return;
+
if (dev->ctl_urb_pending) {
/* URB completion will resubmit */
dev->buzzer_pending = 1;
@@ -562,15 +568,19 @@ static int cm109_input_open(struct input_dev *idev)
dev->ctl_data->byte[HID_OR2] = dev->keybit;
dev->ctl_data->byte[HID_OR3] = 0x00;
+ spin_lock_irq(&dev->ctl_submit_lock);
+ dev->open = 1;
dev->ctl_urb_pending = 1;
- error = usb_submit_urb(dev->urb_ctl, GFP_KERNEL);
- if (!error) {
- dev->open = 1;
- return 0;
+ error = usb_submit_urb(dev->urb_ctl, GFP_ATOMIC);
+ if (error) {
+ dev->ctl_urb_pending = 0;
+ dev->open = 0;
}
- }
+ spin_unlock_irq(&dev->ctl_submit_lock);
- dev->ctl_urb_pending = 0;
+ if (!error)
+ return 0;
+ }
usb_autopm_put_interface(dev->intf);
dev_err(&dev->intf->dev, "%s: usb_submit_urb (urb_ctl) failed %d\n",
@@ -584,13 +594,16 @@ static void cm109_input_close(struct input_dev *idev)
struct cm109_dev *dev = input_get_drvdata(idev);
scoped_guard(mutex, &dev->pm_mutex) {
+ spin_lock_irq(&dev->ctl_submit_lock);
+ dev->open = 0;
+ spin_unlock_irq(&dev->ctl_submit_lock);
+
/*
* 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);
Additional description of the patch:
An analysis of the kernel crash reveals a race condition in the `cm109` input driver that leads to a double-add bug in the USB core's URB enqueueing mechanism.
### Root Cause Analysis
The crash `list_add double add` occurs in `usb_hcd_link_urb_to_ep()` when an URB is submitted while it is already on the endpoint's queue.
In `drivers/input/misc/cm109.c`, the control URB (`dev->urb_ctl`) is submitted in several places:
1. **`cm109_input_open()`**: Called when the input device is opened. It submits the URB to initialize the device without holding the `dev->ctl_submit_lock`.
2. **`cm109_toggle_buzzer_async()`**: The event handler for the input device. It can be triggered at any time by the input core (e.g., via `input_inject_event()` from `kd_mksound()` when a console beep is requested). It acquires `dev->ctl_submit_lock` and submits the same `dev->urb_ctl`.
3. **`cm109_submit_ctl()`**: Called from the completion handler of `dev->urb_irq`. It acquires `dev->ctl_submit_lock` and submits `dev->urb_ctl`.
There are two distinct race conditions that can lead to a double submit:
1. **Race between `cm109_input_open()` and `cm109_toggle_buzzer_async()`**: Because `cm109_input_ev()` does not check if the device is actually open (`dev->open`), and `cm109_input_open()` does not hold the `dev->ctl_submit_lock` when submitting the URB, these two functions can race and call `usb_submit_urb(dev->urb_ctl)` concurrently.
2. **Race between `cm109_submit_ctl()` and `cm109_toggle_buzzer_async()`**: If `cm109_toggle_buzzer_async()` is called while `dev->urb_irq` is in flight, it sees `dev->ctl_urb_pending == 0`, sets it to 1, and submits `dev->urb_ctl`. When `dev->urb_irq` completes, it calls `cm109_submit_ctl()`. However, `cm109_submit_ctl()` does not check `dev->ctl_urb_pending` before submitting `dev->urb_ctl`, leading to a double submit if the URB is already in flight.
### Fix
To fix these bugs, we need to properly serialize URB submissions and state changes using `dev->ctl_submit_lock`:
1. **Serialize URB submission in `cm109_input_open()`**:
Acquire `dev->ctl_submit_lock` before submitting the URB. Because we are holding a spinlock, the memory allocation flag for `usb_submit_urb()` must be changed from `GFP_KERNEL` to `GFP_ATOMIC`.
2. **Check `dev->open` in `cm109_toggle_buzzer_async()`**:
Prevent the event handler from submitting URBs and restarting the polling loop when the device is closed.
3. **Safely update `dev->open`**:
Update the `dev->open` flag under the `dev->ctl_submit_lock` in both `cm109_input_open()` and `cm109_input_close()` to ensure perfect synchronization with the event handler.
4. **Check `dev->ctl_urb_pending` in `cm109_submit_ctl()`**:
If `dev->ctl_urb_pending` is already 1, `cm109_submit_ctl()` must return immediately to prevent a double submit and to avoid modifying the transfer buffer (`dev->ctl_data`) while the URB is in flight.
I have applied these changes to `drivers/input/misc/cm109.c` using the `codeeditor` tool.
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
Reply: Input: cm109 - fix double URB submission race conditions
An analysis of a kernel crash reveals a race condition in the cm109 input driver that leads to a double-add bug in the USB core's URB enqueueing mechanism. The crash `list_add double add` occurs in `usb_hcd_link_urb_to_ep()` when an URB is submitted while it is already on the endpoint's queue.
In `drivers/input/misc/cm109.c`, the control URB (`dev->urb_ctl`) is submitted in several places: `cm109_input_open()`, `cm109_toggle_buzzer_async()`, and `cm109_submit_ctl()`.
There are two distinct race conditions that can lead to a double submit. First, a race between `cm109_input_open()` and `cm109_toggle_buzzer_async()`: because `cm109_input_ev()` does not check if the device is actually open (`dev->open`), and `cm109_input_open()` does not hold the `dev->ctl_submit_lock` when submitting the URB, these two functions can race and call `usb_submit_urb(dev->urb_ctl)` concurrently. Second, a race between `cm109_submit_ctl()` and `cm109_toggle_buzzer_async()`: if `cm109_toggle_buzzer_async()` is called while `dev->urb_irq` is in flight, it sees `dev->ctl_urb_pending == 0`, sets it to 1, and submits `dev->urb_ctl`. When `dev->urb_irq` completes, it calls `cm109_submit_ctl()`. However, `cm109_submit_ctl()` does not check `dev->ctl_urb_pending` before submitting `dev->urb_ctl`, leading to a double submit if the URB is already in flight.
To fix these bugs, we properly serialize URB submissions and state changes using `dev->ctl_submit_lock`. We serialize URB submission in `cm109_input_open()` by acquiring `dev->ctl_submit_lock` before submitting the URB, and changing the memory allocation flag for `usb_submit_urb()` from `GFP_KERNEL` to `GFP_ATOMIC`. We check `dev->open` in `cm109_toggle_buzzer_async()` to prevent the event handler from submitting URBs and restarting the polling loop when the device is closed. We safely update the `dev->open` flag under the `dev->ctl_submit_lock` in both `cm109_input_open()` and `cm109_input_close()` to ensure perfect synchronization with the event handler. Finally, we check `dev->ctl_urb_pending` in `cm109_submit_ctl()` so that if `dev->ctl_urb_pending` is already 1, `cm109_submit_ctl()` returns immediately to prevent a double submit and to avoid modifying the transfer buffer (`dev->ctl_data`) while the URB is in flight.
|
| 106/2 |
2026/05/16 15:02 |
llm |
description-generator |
0m
Model:
Tokens:
input: 9778
output: 705
thoughts: 2047
Thoughts: **My Analysis of the cm109 Double-Add Bug Fix**
Okay, so I've got a patch for the `cm109` input driver that needs a commit description. The user wants a concise one-line summary and a more detailed description. From the information provided and my understanding, it looks like a race condition is causing a double URB submission, leading to a crash in `usb_hcd_link_urb_to_ep()`. The specific crash is identified as a `list_add double add`. The control URB (`dev->urb_ctl`) is being submitted in `cm109_input_open()`, `cm109_toggle_buzzer_async()`, and `cm109_submit_ctl()`. The race conditions seem to stem from the interactions between these functions, particularly the lack of synchronization around URB submissions and the state of the device (`dev->open` and `dev->ctl_urb_pending`).
I see two main race conditions. The first is between `cm109_input_open()` and `cm109_toggle_buzzer_async()`, where `cm109_input_ev()` doesn't check `dev->open` and `cm109_input_open()` doesn't hold `dev->ctl_submit_lock` during the initial URB submission. The second race involves `cm109_submit_ctl()` and `cm109_toggle_buzzer_async()`, where a completion handler could trigger a second submission if the URB is already pending.
The patch addresses these issues by:
1. Serializing URB submissions in `cm109_input_open()` using `dev->ctl_submit_lock` and changing the allocation flag to `GFP_ATOMIC` as it's a spinlock now.
2. Checking `dev->open` in `cm109_toggle_buzzer_async()` to prevent submissions when the device is closed.
3. Safely updating `dev->open` under the lock in both open and close functions.
4. Checking `dev->ctl_urb_pending` in `cm109_submit_ctl()` to avoid double submissions in the completion handler.
So, I'll structure the commit message accordingly.
The summary will be: "Input: cm109 - fix double URB submission race conditions".
The description will detail the race conditions and how the patch fixes them.
I'll make sure to explain clearly the context and the locking mechanism used.
And I'll ensure that the paragraphs are all single lines.
Here's my final draft.
Summary:
Input: cm109 - fix double URB submission race conditions
Description:
An analysis of a kernel crash reveals a race condition in the cm109 input driver that leads to a double-add bug in the USB core's URB enqueueing mechanism. The crash `list_add double add` occurs in `usb_hcd_link_urb_to_ep()` when an URB is submitted while it is already on the endpoint's queue.
In `drivers/input/misc/cm109.c`, the control URB (`dev->urb_ctl`) is submitted in several places: `cm109_input_open()`, `cm109_toggle_buzzer_async()`, and `cm109_submit_ctl()`.
There are two distinct race conditions that can lead to a double submit. First, a race between `cm109_input_open()` and `cm109_toggle_buzzer_async()`: because `cm109_input_ev()` does not check if the device is actually open (`dev->open`), and `cm109_input_open()` does not hold the `dev->ctl_submit_lock` when submitting the URB, these two functions can race and call `usb_submit_urb(dev->urb_ctl)` concurrently. Second, a race between `cm109_submit_ctl()` and `cm109_toggle_buzzer_async()`: if `cm109_toggle_buzzer_async()` is called while `dev->urb_irq` is in flight, it sees `dev->ctl_urb_pending == 0`, sets it to 1, and submits `dev->urb_ctl`. When `dev->urb_irq` completes, it calls `cm109_submit_ctl()`. However, `cm109_submit_ctl()` does not check `dev->ctl_urb_pending` before submitting `dev->urb_ctl`, leading to a double submit if the UR
|
| 107/1 |
2026/05/16 15:03 |
action |
format-patch-description |
0m
Results: map[PatchDescription:Input: cm109 - fix double URB submission race conditions
An analysis of a kernel crash reveals a race condition in the cm109
input driver that leads to a double-add bug in the USB core's URB
enqueueing mechanism. The crash `list_add double add` occurs in
`usb_hcd_link_urb_to_ep()` when an URB is submitted while it is already
on the endpoint's queue.
In `drivers/input/misc/cm109.c`, the control URB (`dev->urb_ctl`) is
submitted in several places: `cm109_input_open()`,
`cm109_toggle_buzzer_async()`, and `cm109_submit_ctl()`.
There are two distinct race conditions that can lead to a double submit.
First, a race between `cm109_input_open()` and
`cm109_toggle_buzzer_async()`: because `cm109_input_ev()` does not check
if the device is actually open (`dev->open`), and `cm109_input_open()`
does not hold the `dev->ctl_submit_lock` when submitting the URB, these
two functions can race and call `usb_submit_urb(dev->urb_ctl)`
concurrently. Second, a race between `cm109_submit_ctl()` and
`cm109_toggle_buzzer_async()`: if `cm109_toggle_buzzer_async()` is
called while `dev->urb_irq` is in flight, it sees `dev->ctl_urb_pending
== 0`, sets it to 1, and submits `dev->urb_ctl`. When `dev->urb_irq`
completes, it calls `cm109_submit_ctl()`. However, `cm109_submit_ctl()`
does not check `dev->ctl_urb_pending` before submitting `dev->urb_ctl`,
leading to a double submit if the URB is already in flight.
To fix these bugs, we properly serialize URB submissions and state
changes using `dev->ctl_submit_lock`. We serialize URB submission in
`cm109_input_open()` by acquiring `dev->ctl_submit_lock` before
submitting the URB, and changing the memory allocation flag for
`usb_submit_urb()` from `GFP_KERNEL` to `GFP_ATOMIC`. We check
`dev->open` in `cm109_toggle_buzzer_async()` to prevent the event
handler from submitting URBs and restarting the polling loop when the
device is closed. We safely update the `dev->open` flag under the
`dev->ctl_submit_lock` in both `cm109_input_open()` and
`cm109_input_close()` to ensure perfect synchronization with the event
handler. Finally, we check `dev->ctl_urb_pending` in
`cm109_submit_ctl()` so that if `dev->ctl_urb_pending` is already 1,
`cm109_submit_ctl()` returns immediately to prevent a double submit and
to avoid modifying the transfer buffer (`dev->ctl_data`) while the URB
is in flight.]
|