1
0
mirror of https://github.com/medusalix/xone.git synced 2024-11-25 05:26:13 +00:00
xone/driver/chatpad.c
2024-01-18 20:44:46 +01:00

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");