diff options
author | David Herrmann <dh.herrmann@googlemail.com> | 2012-06-10 09:16:18 -0400 |
---|---|---|
committer | Jiri Kosina <jkosina@suse.cz> | 2012-06-18 07:42:01 -0400 |
commit | d365c6cfd337a2bccdc65eacce271a311ea1072c (patch) | |
tree | fcd33cd4935c8c1c0acdebaaa62047d35e103e86 /drivers/hid | |
parent | 6664ef72a47459f883d3409ca9b2fa200015704b (diff) |
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 <dh.herrmann@googlemail.com>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
Diffstat (limited to 'drivers/hid')
-rw-r--r-- | drivers/hid/uhid.c | 144 |
1 files changed, 144 insertions, 0 deletions
diff --git a/drivers/hid/uhid.c b/drivers/hid/uhid.c index 31e8379cfd15..61ee7cc32ccf 100644 --- a/drivers/hid/uhid.c +++ b/drivers/hid/uhid.c | |||
@@ -29,6 +29,11 @@ | |||
29 | 29 | ||
30 | struct uhid_device { | 30 | struct uhid_device { |
31 | struct mutex devlock; | 31 | struct mutex devlock; |
32 | bool running; | ||
33 | |||
34 | __u8 *rd_data; | ||
35 | uint rd_size; | ||
36 | |||
32 | struct hid_device *hid; | 37 | struct hid_device *hid; |
33 | struct uhid_event input_buf; | 38 | struct uhid_event input_buf; |
34 | 39 | ||
@@ -75,6 +80,136 @@ static int uhid_queue_event(struct uhid_device *uhid, __u32 event) | |||
75 | return 0; | 80 | return 0; |
76 | } | 81 | } |
77 | 82 | ||
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 | |||
78 | static int uhid_char_open(struct inode *inode, struct file *file) | 213 | static int uhid_char_open(struct inode *inode, struct file *file) |
79 | { | 214 | { |
80 | struct uhid_device *uhid; | 215 | struct uhid_device *uhid; |
@@ -86,6 +221,7 @@ static int uhid_char_open(struct inode *inode, struct file *file) | |||
86 | mutex_init(&uhid->devlock); | 221 | mutex_init(&uhid->devlock); |
87 | spin_lock_init(&uhid->qlock); | 222 | spin_lock_init(&uhid->qlock); |
88 | init_waitqueue_head(&uhid->waitq); | 223 | init_waitqueue_head(&uhid->waitq); |
224 | uhid->running = false; | ||
89 | 225 | ||
90 | file->private_data = uhid; | 226 | file->private_data = uhid; |
91 | nonseekable_open(inode, file); | 227 | nonseekable_open(inode, file); |
@@ -98,6 +234,8 @@ static int uhid_char_release(struct inode *inode, struct file *file) | |||
98 | struct uhid_device *uhid = file->private_data; | 234 | struct uhid_device *uhid = file->private_data; |
99 | unsigned int i; | 235 | unsigned int i; |
100 | 236 | ||
237 | uhid_dev_destroy(uhid); | ||
238 | |||
101 | for (i = 0; i < UHID_BUFSIZE; ++i) | 239 | for (i = 0; i < UHID_BUFSIZE; ++i) |
102 | kfree(uhid->outq[i]); | 240 | kfree(uhid->outq[i]); |
103 | 241 | ||
@@ -177,6 +315,12 @@ static ssize_t uhid_char_write(struct file *file, const char __user *buffer, | |||
177 | } | 315 | } |
178 | 316 | ||
179 | switch (uhid->input_buf.type) { | 317 | 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; | ||
180 | default: | 324 | default: |
181 | ret = -EOPNOTSUPP; | 325 | ret = -EOPNOTSUPP; |
182 | } | 326 | } |