mirror of
https://github.com/medusalix/xone.git
synced 2024-11-25 05:26:13 +00:00
205 lines
4.7 KiB
C
205 lines
4.7 KiB
C
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
/*
|
|
* Copyright (C) 2021 Severin von Wnuck-Lipinski <severinvonw@outlook.de>
|
|
*/
|
|
|
|
#include <linux/module.h>
|
|
#include <linux/hid.h>
|
|
|
|
#include "common.h"
|
|
|
|
#define GIP_CP_NAME "Microsoft Xbox Chatpad"
|
|
|
|
struct gip_chatpad {
|
|
struct gip_client *client;
|
|
struct gip_input input;
|
|
|
|
struct hid_device *hid_dev;
|
|
};
|
|
|
|
static int gip_chatpad_hid_start(struct hid_device *dev)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static void gip_chatpad_hid_stop(struct hid_device *dev)
|
|
{
|
|
}
|
|
|
|
static int gip_chatpad_hid_open(struct hid_device *dev)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static void gip_chatpad_hid_close(struct hid_device *dev)
|
|
{
|
|
}
|
|
|
|
static int gip_chatpad_hid_parse(struct hid_device *dev)
|
|
{
|
|
struct gip_chatpad *chatpad = dev->driver_data;
|
|
struct gip_client *client = chatpad->client;
|
|
struct gip_info_element *desc_info = client->hid_descriptor;
|
|
struct hid_descriptor *desc = (struct hid_descriptor *)desc_info->data;
|
|
|
|
if (desc->bLength < sizeof(*desc) || desc->bNumDescriptors != 1) {
|
|
dev_err(&client->dev, "%s: invalid descriptor\n", __func__);
|
|
return -EINVAL;
|
|
}
|
|
|
|
dev->version = le16_to_cpu(desc->bcdHID);
|
|
dev->country = desc->bCountryCode;
|
|
|
|
return hid_parse_report(dev, desc_info->data + sizeof(*desc),
|
|
desc_info->count - sizeof(*desc));
|
|
}
|
|
|
|
static int gip_chatpad_hid_raw_request(struct hid_device *dev,
|
|
unsigned char report_num, __u8 *buf,
|
|
size_t len, unsigned char report_type,
|
|
int request_type)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static struct hid_ll_driver gip_chatpad_hid_driver = {
|
|
.start = gip_chatpad_hid_start,
|
|
.stop = gip_chatpad_hid_stop,
|
|
.open = gip_chatpad_hid_open,
|
|
.close = gip_chatpad_hid_close,
|
|
.parse = gip_chatpad_hid_parse,
|
|
.raw_request = gip_chatpad_hid_raw_request,
|
|
};
|
|
|
|
static int gip_chatpad_init_input(struct gip_chatpad *chatpad)
|
|
{
|
|
int err;
|
|
|
|
input_set_capability(chatpad->input.dev, EV_KEY, BTN_MODE);
|
|
|
|
err = input_register_device(chatpad->input.dev);
|
|
if (err)
|
|
dev_err(&chatpad->client->dev, "%s: register failed: %d\n",
|
|
__func__, err);
|
|
|
|
return err;
|
|
}
|
|
|
|
static int gip_chatpad_init_hid(struct gip_chatpad *chatpad)
|
|
{
|
|
struct gip_client *client = chatpad->client;
|
|
struct hid_device *dev;
|
|
int err;
|
|
|
|
dev = hid_allocate_device();
|
|
if (IS_ERR(dev)) {
|
|
dev_err(&client->dev, "%s: allocate failed: %ld\n",
|
|
__func__, PTR_ERR(dev));
|
|
return PTR_ERR(dev);
|
|
}
|
|
|
|
dev->bus = BUS_USB;
|
|
dev->vendor = client->hardware.vendor;
|
|
dev->product = client->hardware.product;
|
|
dev->version = client->hardware.version;
|
|
dev->dev.parent = &client->dev;
|
|
dev->ll_driver = &gip_chatpad_hid_driver;
|
|
|
|
strscpy(dev->name, GIP_CP_NAME, sizeof(dev->name));
|
|
snprintf(dev->phys, sizeof(dev->phys), "%s/input1",
|
|
dev_name(&client->dev));
|
|
|
|
dev->driver_data = chatpad;
|
|
|
|
err = hid_add_device(dev);
|
|
if (err) {
|
|
dev_err(&client->dev, "%s: add failed: %d\n", __func__, err);
|
|
hid_destroy_device(dev);
|
|
return err;
|
|
}
|
|
|
|
chatpad->hid_dev = dev;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int gip_chatpad_op_guide_button(struct gip_client *client, bool down)
|
|
{
|
|
struct gip_chatpad *chatpad = dev_get_drvdata(&client->dev);
|
|
|
|
input_report_key(chatpad->input.dev, BTN_MODE, down);
|
|
input_sync(chatpad->input.dev);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int gip_chatpad_op_hid_report(struct gip_client *client,
|
|
void *data, u32 len)
|
|
{
|
|
struct gip_chatpad *chatpad = dev_get_drvdata(&client->dev);
|
|
|
|
return hid_input_report(chatpad->hid_dev, HID_INPUT_REPORT,
|
|
data, len, true);
|
|
}
|
|
|
|
static int gip_chatpad_probe(struct gip_client *client)
|
|
{
|
|
struct gip_chatpad *chatpad;
|
|
struct gip_info_element *hid_desc = client->hid_descriptor;
|
|
int err;
|
|
|
|
if (!hid_desc || hid_desc->count < sizeof(struct hid_descriptor))
|
|
return -ENODEV;
|
|
|
|
chatpad = devm_kzalloc(&client->dev, sizeof(*chatpad), GFP_KERNEL);
|
|
if (!chatpad)
|
|
return -ENOMEM;
|
|
|
|
chatpad->client = client;
|
|
|
|
err = gip_set_power_mode(client, GIP_PWR_ON);
|
|
if (err)
|
|
return err;
|
|
|
|
err = gip_init_input(&chatpad->input, client, GIP_CP_NAME);
|
|
if (err)
|
|
return err;
|
|
|
|
err = gip_chatpad_init_input(chatpad);
|
|
if (err)
|
|
return err;
|
|
|
|
err = gip_chatpad_init_hid(chatpad);
|
|
if (err)
|
|
return err;
|
|
|
|
dev_set_drvdata(&client->dev, chatpad);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void gip_chatpad_remove(struct gip_client *client)
|
|
{
|
|
struct gip_chatpad *chatpad = dev_get_drvdata(&client->dev);
|
|
|
|
hid_destroy_device(chatpad->hid_dev);
|
|
}
|
|
|
|
static struct gip_driver gip_chatpad_driver = {
|
|
.name = "xone-gip-chatpad",
|
|
.class = "Windows.Xbox.Input.Chatpad",
|
|
.ops = {
|
|
.guide_button = gip_chatpad_op_guide_button,
|
|
.hid_report = gip_chatpad_op_hid_report,
|
|
},
|
|
.probe = gip_chatpad_probe,
|
|
.remove = gip_chatpad_remove,
|
|
};
|
|
module_gip_driver(gip_chatpad_driver);
|
|
|
|
MODULE_ALIAS("gip:Windows.Xbox.Input.Chatpad");
|
|
MODULE_AUTHOR("Severin von Wnuck-Lipinski <severinvonw@outlook.de>");
|
|
MODULE_DESCRIPTION("xone GIP chatpad driver");
|
|
MODULE_VERSION("#VERSION#");
|
|
MODULE_LICENSE("GPL");
|