162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Roccat common functions for device specific drivers 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (c) 2011 Stefan Achatz <erazor_de@users.sourceforge.net> 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci/* 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include <linux/hid.h> 1262306a36Sopenharmony_ci#include <linux/slab.h> 1362306a36Sopenharmony_ci#include <linux/module.h> 1462306a36Sopenharmony_ci#include "hid-roccat-common.h" 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_cistatic inline uint16_t roccat_common2_feature_report(uint8_t report_id) 1762306a36Sopenharmony_ci{ 1862306a36Sopenharmony_ci return 0x300 | report_id; 1962306a36Sopenharmony_ci} 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ciint roccat_common2_receive(struct usb_device *usb_dev, uint report_id, 2262306a36Sopenharmony_ci void *data, uint size) 2362306a36Sopenharmony_ci{ 2462306a36Sopenharmony_ci char *buf; 2562306a36Sopenharmony_ci int len; 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci buf = kmalloc(size, GFP_KERNEL); 2862306a36Sopenharmony_ci if (buf == NULL) 2962306a36Sopenharmony_ci return -ENOMEM; 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci len = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0), 3262306a36Sopenharmony_ci HID_REQ_GET_REPORT, 3362306a36Sopenharmony_ci USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN, 3462306a36Sopenharmony_ci roccat_common2_feature_report(report_id), 3562306a36Sopenharmony_ci 0, buf, size, USB_CTRL_SET_TIMEOUT); 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci memcpy(data, buf, size); 3862306a36Sopenharmony_ci kfree(buf); 3962306a36Sopenharmony_ci return ((len < 0) ? len : ((len != size) ? -EIO : 0)); 4062306a36Sopenharmony_ci} 4162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(roccat_common2_receive); 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ciint roccat_common2_send(struct usb_device *usb_dev, uint report_id, 4462306a36Sopenharmony_ci void const *data, uint size) 4562306a36Sopenharmony_ci{ 4662306a36Sopenharmony_ci char *buf; 4762306a36Sopenharmony_ci int len; 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci buf = kmemdup(data, size, GFP_KERNEL); 5062306a36Sopenharmony_ci if (buf == NULL) 5162306a36Sopenharmony_ci return -ENOMEM; 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci len = usb_control_msg(usb_dev, usb_sndctrlpipe(usb_dev, 0), 5462306a36Sopenharmony_ci HID_REQ_SET_REPORT, 5562306a36Sopenharmony_ci USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT, 5662306a36Sopenharmony_ci roccat_common2_feature_report(report_id), 5762306a36Sopenharmony_ci 0, buf, size, USB_CTRL_SET_TIMEOUT); 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci kfree(buf); 6062306a36Sopenharmony_ci return ((len < 0) ? len : ((len != size) ? -EIO : 0)); 6162306a36Sopenharmony_ci} 6262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(roccat_common2_send); 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_cienum roccat_common2_control_states { 6562306a36Sopenharmony_ci ROCCAT_COMMON_CONTROL_STATUS_CRITICAL = 0, 6662306a36Sopenharmony_ci ROCCAT_COMMON_CONTROL_STATUS_OK = 1, 6762306a36Sopenharmony_ci ROCCAT_COMMON_CONTROL_STATUS_INVALID = 2, 6862306a36Sopenharmony_ci ROCCAT_COMMON_CONTROL_STATUS_BUSY = 3, 6962306a36Sopenharmony_ci ROCCAT_COMMON_CONTROL_STATUS_CRITICAL_NEW = 4, 7062306a36Sopenharmony_ci}; 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_cistatic int roccat_common2_receive_control_status(struct usb_device *usb_dev) 7362306a36Sopenharmony_ci{ 7462306a36Sopenharmony_ci int retval; 7562306a36Sopenharmony_ci struct roccat_common2_control control; 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci do { 7862306a36Sopenharmony_ci msleep(50); 7962306a36Sopenharmony_ci retval = roccat_common2_receive(usb_dev, 8062306a36Sopenharmony_ci ROCCAT_COMMON_COMMAND_CONTROL, 8162306a36Sopenharmony_ci &control, sizeof(struct roccat_common2_control)); 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci if (retval) 8462306a36Sopenharmony_ci return retval; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci switch (control.value) { 8762306a36Sopenharmony_ci case ROCCAT_COMMON_CONTROL_STATUS_OK: 8862306a36Sopenharmony_ci return 0; 8962306a36Sopenharmony_ci case ROCCAT_COMMON_CONTROL_STATUS_BUSY: 9062306a36Sopenharmony_ci msleep(500); 9162306a36Sopenharmony_ci continue; 9262306a36Sopenharmony_ci case ROCCAT_COMMON_CONTROL_STATUS_INVALID: 9362306a36Sopenharmony_ci case ROCCAT_COMMON_CONTROL_STATUS_CRITICAL: 9462306a36Sopenharmony_ci case ROCCAT_COMMON_CONTROL_STATUS_CRITICAL_NEW: 9562306a36Sopenharmony_ci return -EINVAL; 9662306a36Sopenharmony_ci default: 9762306a36Sopenharmony_ci dev_err(&usb_dev->dev, 9862306a36Sopenharmony_ci "roccat_common2_receive_control_status: " 9962306a36Sopenharmony_ci "unknown response value 0x%x\n", 10062306a36Sopenharmony_ci control.value); 10162306a36Sopenharmony_ci return -EINVAL; 10262306a36Sopenharmony_ci } 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci } while (1); 10562306a36Sopenharmony_ci} 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ciint roccat_common2_send_with_status(struct usb_device *usb_dev, 10862306a36Sopenharmony_ci uint command, void const *buf, uint size) 10962306a36Sopenharmony_ci{ 11062306a36Sopenharmony_ci int retval; 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci retval = roccat_common2_send(usb_dev, command, buf, size); 11362306a36Sopenharmony_ci if (retval) 11462306a36Sopenharmony_ci return retval; 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci msleep(100); 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci return roccat_common2_receive_control_status(usb_dev); 11962306a36Sopenharmony_ci} 12062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(roccat_common2_send_with_status); 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ciint roccat_common2_device_init_struct(struct usb_device *usb_dev, 12362306a36Sopenharmony_ci struct roccat_common2_device *dev) 12462306a36Sopenharmony_ci{ 12562306a36Sopenharmony_ci mutex_init(&dev->lock); 12662306a36Sopenharmony_ci return 0; 12762306a36Sopenharmony_ci} 12862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(roccat_common2_device_init_struct); 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_cissize_t roccat_common2_sysfs_read(struct file *fp, struct kobject *kobj, 13162306a36Sopenharmony_ci char *buf, loff_t off, size_t count, 13262306a36Sopenharmony_ci size_t real_size, uint command) 13362306a36Sopenharmony_ci{ 13462306a36Sopenharmony_ci struct device *dev = kobj_to_dev(kobj)->parent->parent; 13562306a36Sopenharmony_ci struct roccat_common2_device *roccat_dev = hid_get_drvdata(dev_get_drvdata(dev)); 13662306a36Sopenharmony_ci struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev)); 13762306a36Sopenharmony_ci int retval; 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci if (off >= real_size) 14062306a36Sopenharmony_ci return 0; 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci if (off != 0 || count != real_size) 14362306a36Sopenharmony_ci return -EINVAL; 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci mutex_lock(&roccat_dev->lock); 14662306a36Sopenharmony_ci retval = roccat_common2_receive(usb_dev, command, buf, real_size); 14762306a36Sopenharmony_ci mutex_unlock(&roccat_dev->lock); 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci return retval ? retval : real_size; 15062306a36Sopenharmony_ci} 15162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(roccat_common2_sysfs_read); 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_cissize_t roccat_common2_sysfs_write(struct file *fp, struct kobject *kobj, 15462306a36Sopenharmony_ci void const *buf, loff_t off, size_t count, 15562306a36Sopenharmony_ci size_t real_size, uint command) 15662306a36Sopenharmony_ci{ 15762306a36Sopenharmony_ci struct device *dev = kobj_to_dev(kobj)->parent->parent; 15862306a36Sopenharmony_ci struct roccat_common2_device *roccat_dev = hid_get_drvdata(dev_get_drvdata(dev)); 15962306a36Sopenharmony_ci struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev)); 16062306a36Sopenharmony_ci int retval; 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci if (off != 0 || count != real_size) 16362306a36Sopenharmony_ci return -EINVAL; 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci mutex_lock(&roccat_dev->lock); 16662306a36Sopenharmony_ci retval = roccat_common2_send_with_status(usb_dev, command, buf, real_size); 16762306a36Sopenharmony_ci mutex_unlock(&roccat_dev->lock); 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci return retval ? retval : real_size; 17062306a36Sopenharmony_ci} 17162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(roccat_common2_sysfs_write); 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ciMODULE_AUTHOR("Stefan Achatz"); 17462306a36Sopenharmony_ciMODULE_DESCRIPTION("USB Roccat common driver"); 17562306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 176