Skip to content

Commit d365c6c

Browse files
David HerrmannJiri Kosina
authored andcommitted
HID: uhid: add UHID_CREATE and UHID_DESTROY events
UHID_CREATE and UHID_DESTROY are used to create and destroy a device on an open uhid char-device. Internally, we allocate and register an HID device with the HID core and immediately start the device. From now on events may be received or sent to the device. The UHID_CREATE event has a payload similar to the data used by Bluetooth-HIDP when creating a new connection. Signed-off-by: David Herrmann <[email protected]> Signed-off-by: Jiri Kosina <[email protected]>
1 parent 6664ef7 commit d365c6c

2 files changed

Lines changed: 164 additions & 1 deletion

File tree

drivers/hid/uhid.c

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,11 @@
2929

3030
struct uhid_device {
3131
struct mutex devlock;
32+
bool running;
33+
34+
__u8 *rd_data;
35+
uint rd_size;
36+
3237
struct hid_device *hid;
3338
struct uhid_event input_buf;
3439

@@ -75,6 +80,136 @@ static int uhid_queue_event(struct uhid_device *uhid, __u32 event)
7580
return 0;
7681
}
7782

83+
static int uhid_hid_start(struct hid_device *hid)
84+
{
85+
return 0;
86+
}
87+
88+
static void uhid_hid_stop(struct hid_device *hid)
89+
{
90+
}
91+
92+
static int uhid_hid_open(struct hid_device *hid)
93+
{
94+
return 0;
95+
}
96+
97+
static void uhid_hid_close(struct hid_device *hid)
98+
{
99+
}
100+
101+
static int uhid_hid_input(struct input_dev *input, unsigned int type,
102+
unsigned int code, int value)
103+
{
104+
return 0;
105+
}
106+
107+
static int uhid_hid_parse(struct hid_device *hid)
108+
{
109+
return 0;
110+
}
111+
112+
static int uhid_hid_get_raw(struct hid_device *hid, unsigned char rnum,
113+
__u8 *buf, size_t count, unsigned char rtype)
114+
{
115+
return 0;
116+
}
117+
118+
static int uhid_hid_output_raw(struct hid_device *hid, __u8 *buf, size_t count,
119+
unsigned char report_type)
120+
{
121+
return 0;
122+
}
123+
124+
static struct hid_ll_driver uhid_hid_driver = {
125+
.start = uhid_hid_start,
126+
.stop = uhid_hid_stop,
127+
.open = uhid_hid_open,
128+
.close = uhid_hid_close,
129+
.hidinput_input_event = uhid_hid_input,
130+
.parse = uhid_hid_parse,
131+
};
132+
133+
static int uhid_dev_create(struct uhid_device *uhid,
134+
const struct uhid_event *ev)
135+
{
136+
struct hid_device *hid;
137+
int ret;
138+
139+
if (uhid->running)
140+
return -EALREADY;
141+
142+
uhid->rd_size = ev->u.create.rd_size;
143+
if (uhid->rd_size <= 0 || uhid->rd_size > HID_MAX_DESCRIPTOR_SIZE)
144+
return -EINVAL;
145+
146+
uhid->rd_data = kmalloc(uhid->rd_size, GFP_KERNEL);
147+
if (!uhid->rd_data)
148+
return -ENOMEM;
149+
150+
if (copy_from_user(uhid->rd_data, ev->u.create.rd_data,
151+
uhid->rd_size)) {
152+
ret = -EFAULT;
153+
goto err_free;
154+
}
155+
156+
hid = hid_allocate_device();
157+
if (IS_ERR(hid)) {
158+
ret = PTR_ERR(hid);
159+
goto err_free;
160+
}
161+
162+
strncpy(hid->name, ev->u.create.name, 127);
163+
hid->name[127] = 0;
164+
strncpy(hid->phys, ev->u.create.phys, 63);
165+
hid->phys[63] = 0;
166+
strncpy(hid->uniq, ev->u.create.uniq, 63);
167+
hid->uniq[63] = 0;
168+
169+
hid->ll_driver = &uhid_hid_driver;
170+
hid->hid_get_raw_report = uhid_hid_get_raw;
171+
hid->hid_output_raw_report = uhid_hid_output_raw;
172+
hid->bus = ev->u.create.bus;
173+
hid->vendor = ev->u.create.vendor;
174+
hid->product = ev->u.create.product;
175+
hid->version = ev->u.create.version;
176+
hid->country = ev->u.create.country;
177+
hid->driver_data = uhid;
178+
hid->dev.parent = uhid_misc.this_device;
179+
180+
uhid->hid = hid;
181+
uhid->running = true;
182+
183+
ret = hid_add_device(hid);
184+
if (ret) {
185+
hid_err(hid, "Cannot register HID device\n");
186+
goto err_hid;
187+
}
188+
189+
return 0;
190+
191+
err_hid:
192+
hid_destroy_device(hid);
193+
uhid->hid = NULL;
194+
uhid->running = false;
195+
err_free:
196+
kfree(uhid->rd_data);
197+
return ret;
198+
}
199+
200+
static int uhid_dev_destroy(struct uhid_device *uhid)
201+
{
202+
if (!uhid->running)
203+
return -EINVAL;
204+
205+
uhid->running = false;
206+
207+
hid_destroy_device(uhid->hid);
208+
kfree(uhid->rd_data);
209+
210+
return 0;
211+
}
212+
78213
static int uhid_char_open(struct inode *inode, struct file *file)
79214
{
80215
struct uhid_device *uhid;
@@ -86,6 +221,7 @@ static int uhid_char_open(struct inode *inode, struct file *file)
86221
mutex_init(&uhid->devlock);
87222
spin_lock_init(&uhid->qlock);
88223
init_waitqueue_head(&uhid->waitq);
224+
uhid->running = false;
89225

90226
file->private_data = uhid;
91227
nonseekable_open(inode, file);
@@ -98,6 +234,8 @@ static int uhid_char_release(struct inode *inode, struct file *file)
98234
struct uhid_device *uhid = file->private_data;
99235
unsigned int i;
100236

237+
uhid_dev_destroy(uhid);
238+
101239
for (i = 0; i < UHID_BUFSIZE; ++i)
102240
kfree(uhid->outq[i]);
103241

@@ -177,6 +315,12 @@ static ssize_t uhid_char_write(struct file *file, const char __user *buffer,
177315
}
178316

179317
switch (uhid->input_buf.type) {
318+
case UHID_CREATE:
319+
ret = uhid_dev_create(uhid, &uhid->input_buf);
320+
break;
321+
case UHID_DESTROY:
322+
ret = uhid_dev_destroy(uhid);
323+
break;
180324
default:
181325
ret = -EOPNOTSUPP;
182326
}

include/linux/uhid.h

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,30 @@
2323
#include <linux/types.h>
2424

2525
enum uhid_event_type {
26-
UHID_DUMMY,
26+
UHID_CREATE,
27+
UHID_DESTROY,
2728
};
2829

30+
struct uhid_create_req {
31+
__u8 name[128];
32+
__u8 phys[64];
33+
__u8 uniq[64];
34+
__u8 __user *rd_data;
35+
__u16 rd_size;
36+
37+
__u16 bus;
38+
__u32 vendor;
39+
__u32 product;
40+
__u32 version;
41+
__u32 country;
42+
} __attribute__((__packed__));
43+
2944
struct uhid_event {
3045
__u32 type;
46+
47+
union {
48+
struct uhid_create_req create;
49+
} u;
3150
} __attribute__((__packed__));
3251

3352
#endif /* __UHID_H_ */

0 commit comments

Comments
 (0)