// SPDX-License-Identifier: GPL-2.0-or-later /* * Roccat driver for Linux * * Copyright (c) 2010 Stefan Achatz <erazor_de@users.sourceforge.net>
*/
/*
*/
/* * Module roccat is a char device used to report special events of roccat * hardware to userland. These events include requests for on-screen-display of * profile or dpi settings or requests for execution of macro sequences that are * not stored in device. The information in these events depends on hid device * implementation and contains data that is not available in a single hid event * or else hidraw could have been used. * It is inspired by hidraw, but uses only one circular buffer for all readers.
*/
/* should be a power of 2 for performance reason */ #define ROCCAT_CBUF_SIZE 16
struct roccat_report {
uint8_t *value;
};
struct roccat_device { unsignedint minor; int report_size; int open; int exist;
wait_queue_head_t wait; struct device *dev; struct hid_device *hid; struct list_head readers; /* protects modifications of readers list */ struct mutex readers_lock;
/* * circular_buffer has one writer and multiple readers with their own * read pointers
*/ struct roccat_report cbuf[ROCCAT_CBUF_SIZE]; int cbuf_end; struct mutex cbuf_lock;
};
/* here we either have data or a reason to return if retval is set */ if (retval) goto exit_unlock;
report = &device->cbuf[reader->cbuf_start]; /* * If report is larger than requested amount of data, rest of report * is lost!
*/
len = device->report_size > count ? count : device->report_size;
reader = kzalloc(sizeof(struct roccat_reader), GFP_KERNEL); if (!reader) return -ENOMEM;
mutex_lock(&devices_lock);
device = devices[minor];
if (!device) {
pr_emerg("roccat device with minor %d doesn't exist\n", minor);
error = -ENODEV; goto exit_err_devices;
}
mutex_lock(&device->readers_lock);
if (!device->open++) { /* power on device on adding first reader */
error = hid_hw_power(device->hid, PM_HINT_FULLON); if (error < 0) {
--device->open; goto exit_err_readers;
}
if (!--device->open) { /* removing last reader */ if (device->exist) {
hid_hw_power(device->hid, PM_HINT_NORMAL);
hid_hw_close(device->hid);
} else {
kfree(device);
}
}
mutex_unlock(&devices_lock);
return 0;
}
/* * roccat_report_event() - output data to readers * @minor: minor device number returned by roccat_connect() * @data: pointer to data * * Return value is zero on success, a negative error code on failure. * * This is called from interrupt handler.
*/ int roccat_report_event(int minor, u8 const *data)
{ struct roccat_device *device; struct roccat_reader *reader; struct roccat_report *report;
uint8_t *new_value;
device = devices[minor];
new_value = kmemdup(data, device->report_size, GFP_ATOMIC); if (!new_value) return -ENOMEM;
list_for_each_entry(reader, &device->readers, node) { /* * As we already inserted one element, the buffer can't be * empty. If start and end are equal, buffer is full and we * increase start, so that slow reader misses one event, but * gets the newer ones in the right order.
*/ if (reader->cbuf_start == device->cbuf_end)
reader->cbuf_start = (reader->cbuf_start + 1) % ROCCAT_CBUF_SIZE;
}
/* * roccat_connect() - create a char device for special event output * @class: the class thats used to create the device. Meant to hold device * specific sysfs attributes. * @hid: the hid device the char device should be connected to. * @report_size: size of reports * * Return value is minor device number in Range [0, ROCCAT_MAX_DEVICES] on * success, a negative error code on failure.
*/ int roccat_connect(conststructclass *klass, struct hid_device *hid, int report_size)
{ unsignedint minor; struct roccat_device *device; int temp;
device = kzalloc(sizeof(struct roccat_device), GFP_KERNEL); if (!device) return -ENOMEM;
mutex_lock(&devices_lock);
for (minor = 0; minor < ROCCAT_MAX_DEVICES; ++minor) { if (devices[minor]) continue; break;
}
/* roccat_disconnect() - remove char device from hid device * @minor: the minor device number returned by roccat_connect()
*/ void roccat_disconnect(int minor)
{ struct roccat_device *device;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.