blob: 393f247fead55f4d570cf5ce86e03cfa707b93bb [file] [log] [blame]
Tai Kuof81f4a12021-02-11 16:22:44 +08001// SPDX-License-Identifier: GPL-2.0
2
3#include <linux/module.h>
4#include <linux/fs.h>
5#include <linux/slab.h>
6#include <linux/uaccess.h>
7#include <linux/poll.h>
8
9#include "touch_offload.h"
10
11static int touch_offload_open(struct inode *inode, struct file *file)
12{
13 struct touch_offload_context *context =
14 container_of(inode->i_cdev, struct touch_offload_context, dev);
15
16 file->private_data = context;
17 pr_debug("%s\n", __func__);
18
19 mutex_lock(&context->file_lock);
20 if (context->file_in_use) {
21 mutex_unlock(&context->file_lock);
22 return -EBUSY;
23 }
24 context->file_in_use = true;
25 mutex_unlock(&context->file_lock);
26
27 /* Prepare context for consumer. E.g., event queue */
28 return 0;
29}
30
31static int touch_offload_release(struct inode *inode, struct file *file)
32{
33 struct touch_offload_context *context = file->private_data;
34
35 mutex_lock(&context->file_lock);
36 context->file_in_use = false;
37 mutex_unlock(&context->file_lock);
38
39 pr_debug("%s\n", __func__);
40 return 0;
41}
42
43static int pack_frame(struct touch_offload_context *context,
44 struct touch_offload_frame *frame, char **packed)
45{
46 /* TODO: compute precise size of a packed frame */
47 int max_packed_frame_size =
48 sizeof(struct touch_offload_frame) +
49 TOUCH_OFFLOAD_DATA_SIZE_2D(context->caps.rx_size,
50 context->caps.tx_size) *
51 MAX_CHANNELS;
52 /* TODO: preallocate memory for a single packed frame */
53 char *packed_mem = NULL;
54 char *ptr = NULL;
55 int channel_size;
56 int i = 0;
57
58 if (!frame || frame->num_channels > MAX_CHANNELS)
59 return -EINVAL;
60
61 packed_mem = kzalloc(max_packed_frame_size, GFP_KERNEL);
62 if (packed_mem == NULL)
63 return -ENOMEM;
64 ptr = packed_mem;
65
66 /* Copy the header */
67
68 memcpy(ptr, &frame->header, sizeof(frame->header));
69 ptr += sizeof(frame->header);
70
71 /* Copy frame data */
72
73 for (i = 0; i < frame->num_channels; i++) {
74 if (frame->channel_type[i] == TOUCH_DATA_TYPE_COORD)
75 channel_size = TOUCH_OFFLOAD_FRAME_SIZE_COORD;
76 else if ((frame->channel_type[i] & TOUCH_SCAN_TYPE_MUTUAL) != 0)
77 channel_size =
78 TOUCH_OFFLOAD_FRAME_SIZE_2D(context->caps.rx_size,
79 context->caps.tx_size);
80 else if ((frame->channel_type[i] & TOUCH_SCAN_TYPE_SELF) != 0)
81 channel_size =
82 TOUCH_OFFLOAD_FRAME_SIZE_1D(context->caps.rx_size,
83 context->caps.tx_size);
Steve Pfetschdf0f0a62022-01-13 19:07:27 +000084 else if (frame->channel_type[i] ==
85 CONTEXT_CHANNEL_TYPE_DRIVER_STATUS)
86 channel_size = TOUCH_OFFLOAD_FRAME_SIZE_DRIVER_STATUS;
87 else if (frame->channel_type[i] ==
88 CONTEXT_CHANNEL_TYPE_STYLUS_STATUS)
89 channel_size = TOUCH_OFFLOAD_FRAME_SIZE_STYLUS_STATUS;
Tai Kuof81f4a12021-02-11 16:22:44 +080090 else {
91 pr_err("%s: Invalid channel_type = 0x%08X", __func__,
92 frame->channel_type[i]);
93 kfree(packed_mem);
94 return -EINVAL;
95 }
96 memcpy(ptr, frame->channel_data[i], channel_size);
97 ptr += channel_size;
98 }
99
100 *packed = packed_mem;
101 return (ptr - packed_mem);
102}
103
104static ssize_t touch_offload_read(struct file *file, char __user *user_buffer,
105 size_t size, loff_t *offset)
106{
107 struct touch_offload_context *context = file->private_data;
108 struct touch_offload_frame *frame;
109 size_t copy_size;
110 int result;
111 unsigned long remaining;
112
113 pr_debug("%s\n", __func__);
114
115 if (context->num_buffers == 0)
116 return -EINVAL;
117
118 /* Block until touch events occur */
119 /* Block (on completion?) until len >= 0. */
120 /* Lock the event buffer */
121 /* Copy contents of event buffer to service */
122 /* Reset completion since len=0 */
123 /* Unlock and return */
124
125 /* If the end of the data is reached, free the packed_frame and
126 * return 0
127 */
128
129 mutex_lock(&context->buffer_lock);
130
131 if (context->packed_frame != NULL &&
132 *offset == context->packed_frame_size) {
133 pr_err("%s: [Unexpected!] The buffer should have been recycled after the previous read.\n",
134 __func__);
135 kfree(context->packed_frame);
136 context->packed_frame = NULL;
137 *offset = 0;
138 mutex_unlock(&context->buffer_lock);
139 return 0;
140 } else if (context->packed_frame == NULL) {
141 /* Process the next queued buffer */
142
143 while (list_empty(&context->frame_queue)) {
144 /* Presumably more buffers on the way, so block */
145 mutex_unlock(&context->buffer_lock);
146
147 /* Non-blocking read? */
148 if (file->f_flags & O_NONBLOCK)
149 return -EAGAIN;
150
151 /* Block until data is available */
152 if (wait_event_interruptible(context->read_queue,
153 !list_empty(&context->frame_queue)))
154 return -ERESTARTSYS;
155
156 /* Check that the pipeline is still running */
157 mutex_lock(&context->buffer_lock);
158 }
159
160 frame = list_entry(context->frame_queue.next,
161 struct touch_offload_frame, entry);
162 list_del(&frame->entry);
163
164 result = pack_frame(context, frame, &context->packed_frame);
165 list_add_tail(&frame->entry, &context->free_pool);
166 if (result <= 0) {
167 pr_err("%s: Error packing frame! Result = %d.\n",
168 __func__, result);
169 mutex_unlock(&context->buffer_lock);
170 return -EINVAL;
171 }
172 if (result != context->packed_frame_size) {
173 pr_err("%s: Packed frame size (%d) does not match size allocated per frame(%d)!\n",
174 __func__, result, context->packed_frame_size);
175 }
176
177 }
178
179 /* Transfer the maximum amount of data */
180 copy_size = min((long long)size, context->packed_frame_size - *offset);
181 remaining = copy_to_user(user_buffer, context->packed_frame + *offset,
182 copy_size);
183 if (remaining != 0)
184 pr_err("%s: copy_to_user unexpectedly failed to copy %lu bytes.\n",
185 __func__, remaining);
186 *offset += copy_size - remaining;
187
188 /* Recycle the frame if transfer was complete */
189 if (*offset == context->packed_frame_size) {
190 kfree(context->packed_frame);
191 context->packed_frame = NULL;
192 *offset = 0;
193 }
194
195 mutex_unlock(&context->buffer_lock);
196
197 return copy_size - remaining;
198}
199
200static unsigned int touch_offload_poll(struct file *file,
201 struct poll_table_struct *wait)
202{
203 struct touch_offload_context *context = file->private_data;
204 unsigned int flags = 0;
205
206 pr_debug("%s\n", __func__);
207
208 poll_wait(file, &context->read_queue, wait);
209
210 mutex_lock(&context->buffer_lock);
211
212 if (context->packed_frame || !list_empty(&context->frame_queue))
213 flags |= POLLIN | POLLRDNORM;
214
215 mutex_unlock(&context->buffer_lock);
216 return flags;
217}
218
219static int touch_offload_allocate_buffers(struct touch_offload_context *context,
220 int nb)
221{
222 struct touch_offload_frame *frame = NULL;
223 int i;
224 int num_channels;
225 __u32 mask;
226 __u32 size = 0;
227
228 pr_debug("%s\n", __func__);
229
230 num_channels = (context->config.read_coords ? 1 : 0) +
231 hweight_long(context->config.mutual_data_types) +
Steve Pfetschdf0f0a62022-01-13 19:07:27 +0000232 hweight_long(context->config.self_data_types) +
233 hweight_long(context->config.context_channel_types);
Tai Kuof81f4a12021-02-11 16:22:44 +0800234 if (num_channels == 0 || num_channels > MAX_CHANNELS) {
235 pr_err("%s: Configuration enables more (%d) than %d channels!\n",
236 __func__, num_channels, MAX_CHANNELS);
237 return -EINVAL;
238 }
239
240
241 mutex_lock(&context->buffer_lock);
242
243 /* Add new buffers to the free_pool */
244 for (i = 0; i < nb; i++) {
245 int chan = 0;
246 struct touch_offload_frame *frame =
247 kzalloc(sizeof(struct touch_offload_frame), GFP_KERNEL);
248
249 if (frame == NULL) {
250 mutex_unlock(&context->buffer_lock);
251 return -ENOMEM;
252 }
253
254 frame->header.frame_size = sizeof(frame->header);
255
256 /* Allocate component buffers */
257
258 if (context->config.read_coords) {
259 struct TouchOffloadDataCoord *coord;
260
261 frame->channel_type[chan] = TOUCH_DATA_TYPE_COORD;
262 size = TOUCH_OFFLOAD_FRAME_SIZE_COORD;
263 frame->channel_data[chan] = kzalloc(size,
264 GFP_KERNEL);
265 if (frame->channel_data[chan] == NULL)
266 goto kzalloc_channel_fail;
267 coord = (struct TouchOffloadDataCoord *)
268 frame->channel_data[chan];
269 coord->header.channel_type = TOUCH_DATA_TYPE_COORD;
270 coord->header.channel_size = size;
271 frame->channel_data_size[chan] = size;
272 frame->header.frame_size += size;
273 chan++;
274 }
275 for (mask = 0x01; mask < 0x100; mask <<= 1) {
276 if ((context->config.mutual_data_types & mask) != 0) {
277 struct TouchOffloadData2d *data;
278
279 frame->channel_type[chan] =
280 TOUCH_SCAN_TYPE_MUTUAL | mask;
281 size = TOUCH_OFFLOAD_FRAME_SIZE_2D(
282 context->caps.rx_size,
283 context->caps.tx_size);
284 frame->channel_data[chan] = kzalloc(size,
285 GFP_KERNEL);
286 if (frame->channel_data[chan] == NULL)
287 goto kzalloc_channel_fail;
288 data = (struct TouchOffloadData2d *)
289 frame->channel_data[chan];
290 data->header.channel_type =
291 TOUCH_SCAN_TYPE_MUTUAL | mask;
292 data->header.channel_size = size;
293 frame->channel_data_size[chan] = size;
294 frame->header.frame_size += size;
295 chan++;
296 }
297 }
298 for (mask = 0x01; mask < 0x100; mask <<= 1) {
299 if ((context->config.self_data_types & mask) != 0) {
300 struct TouchOffloadData1d *data;
301
302 frame->channel_type[chan] =
303 TOUCH_SCAN_TYPE_SELF | mask;
304 size = TOUCH_OFFLOAD_FRAME_SIZE_1D(
305 context->caps.rx_size,
306 context->caps.tx_size);
307 frame->channel_data[chan] = kzalloc(size,
308 GFP_KERNEL);
309 if (frame->channel_data[chan] == NULL)
310 goto kzalloc_channel_fail;
311 data = (struct TouchOffloadData1d *)
312 frame->channel_data[chan];
313 data->header.channel_type =
314 TOUCH_SCAN_TYPE_SELF | mask;
315 data->header.channel_size = size;
316 frame->channel_data_size[chan] = size;
317 frame->header.frame_size += size;
318 chan++;
319 }
320 }
Steve Pfetschdf0f0a62022-01-13 19:07:27 +0000321 for (mask = CONTEXT_CHANNEL_BIT_START;
322 mask <= CONTEXT_CHANNEL_BIT_END;
323 mask <<= 1) {
324 struct TouchOffloadChannelHeader *chan_header;
325
326 if (!(context->config.context_channel_types & mask))
327 continue;
328
329 frame->channel_type[chan] = mask;
330 size = 0;
331 switch (mask) {
332 case CONTEXT_CHANNEL_TYPE_DRIVER_STATUS:
333 size =
334 TOUCH_OFFLOAD_FRAME_SIZE_DRIVER_STATUS;
335 break;
336 case CONTEXT_CHANNEL_TYPE_STYLUS_STATUS:
337 size =
338 TOUCH_OFFLOAD_FRAME_SIZE_STYLUS_STATUS;
339 break;
340 default:
341 pr_err("%s: Invalid channel_type = 0x%08X",
342 __func__, mask);
343 goto invalid_context_channel;
344 }
345 frame->channel_data[chan] = kzalloc(size,
346 GFP_KERNEL);
347 if (frame->channel_data[chan] == NULL)
348 goto kzalloc_channel_fail;
349
350 chan_header =
351 (struct TouchOffloadChannelHeader *)
352 frame->channel_data[chan];
353 chan_header->channel_type = mask;
354 chan_header->channel_size = size;
355 frame->channel_data_size[chan] = size;
356 frame->header.frame_size += size;
357 chan++;
358 }
Tai Kuof81f4a12021-02-11 16:22:44 +0800359
360 frame->num_channels = chan;
361 frame->header.num_channels = chan;
362
363 if (context->packed_frame_size == 0)
364 context->packed_frame_size = frame->header.frame_size;
365 if (context->packed_frame_size != frame->header.frame_size)
366 pr_err("%s: Frame size mismatch! %d != %d.\n", __func__,
367 context->packed_frame_size,
368 frame->header.frame_size);
369
370 list_add_tail(&frame->entry, &context->free_pool);
371 context->num_buffers++;
372 }
373
374 mutex_unlock(&context->buffer_lock);
375 return 0;
376
Steve Pfetschdf0f0a62022-01-13 19:07:27 +0000377invalid_context_channel:
Tai Kuof81f4a12021-02-11 16:22:44 +0800378kzalloc_channel_fail:
379 /* Free all channels of "frame" before returning */
380 if (frame)
381 for (i = 0; i < MAX_CHANNELS; i++)
382 kfree(frame->channel_data[i]);
383 kfree(frame);
384
385 mutex_unlock(&context->buffer_lock);
386 return -ENOMEM;
387}
388
389static int touch_offload_free_buffers(struct touch_offload_context *context)
390{
391 int freed = 0;
392 int chan = 0;
393
394 pr_debug("%s\n", __func__);
395
396 mutex_lock(&context->buffer_lock);
397
398 /* Ensure there is no outstanding reserved_frame before continuing */
399 while (context->reserved_frame != NULL) {
400 mutex_unlock(&context->buffer_lock);
401 wait_for_completion(&context->reserve_returned);
402 mutex_lock(&context->buffer_lock);
403 }
404
405 if (context->num_buffers > 0) {
406 while (!list_empty(&context->free_pool)) {
407 struct list_head *next = context->free_pool.next;
408 struct touch_offload_frame *frame =
409 list_entry(next, struct touch_offload_frame, entry);
410
411 list_del(next);
412 for (chan = 0; chan < frame->num_channels; chan++)
413 kfree(frame->channel_data[chan]);
414 kfree(frame);
415 freed++;
416 }
417
418 while (!list_empty(&context->frame_queue)) {
419 struct list_head *next = context->frame_queue.next;
420 struct touch_offload_frame *frame =
421 list_entry(next, struct touch_offload_frame, entry);
422
423 list_del(next);
424 for (chan = 0; chan < frame->num_channels; chan++)
425 kfree(frame->channel_data[chan]);
426 kfree(frame);
427 freed++;
428 }
429 }
430 if (freed != context->num_buffers)
431 pr_err("%s: mismatch between the number of buffers allocated(%d) and freed(%d)!",
432 __func__, context->num_buffers, freed);
433
434 /* clean up */
435 context->num_buffers = 0;
436 context->reserved_frame = NULL;
437 INIT_LIST_HEAD(&context->free_pool);
438 INIT_LIST_HEAD(&context->frame_queue);
439 context->packed_frame_size = 0;
440
441 mutex_unlock(&context->buffer_lock);
442
443 return 0;
444}
445
446static long touch_offload_ioctl(struct file *file, unsigned int ioctl_num,
447 unsigned long ioctl_param)
448{
449 struct touch_offload_context *context = file->private_data;
450 unsigned long err = 0;
451
452 pr_debug("%s: ioctl_num=0x%08X, ioctl_param=0x%08lX\n", __func__,
453 ioctl_num, ioctl_param);
454
455 switch (ioctl_num) {
456 case TOUCH_OFFLOAD_IOC_RD_GETCAPS:
457 {
458 struct TouchOffloadIocGetCaps getCaps;
459
460 /* Copy previously-populated caps */
461 memcpy(&getCaps.caps, &context->caps,
462 sizeof(context->caps));
463
464 err = copy_to_user((void *)ioctl_param, &getCaps,
465 sizeof(getCaps));
466 if (err != 0) {
467 pr_err("%s: copy_to_failed with err=0x%08lX",
468 __func__, err);
469 return err;
470 }
471 break;
472 }
473
474 case TOUCH_OFFLOAD_IOC_WR_CONFIGURE:
475 {
476 struct TouchOffloadIocConfigure configure;
477 int NUM_BUFFERS = 4;
478 int num_channels;
479
480 err = copy_from_user(&configure, (void *)ioctl_param,
481 sizeof(configure));
482 if (err != 0) {
483 pr_err("%s: copy_from_user failed with err=0x%08lX",
484 __func__, err);
485 return err;
486 }
487
488 /* TODO: stop any active streaming */
489
490 /* Purge any previously-allocated buffers */
491 touch_offload_free_buffers(context);
492
493 memset(&context->config, 0, sizeof(context->config));
494 if ((configure.config.continuous_reporting &&
495 !context->caps.continuous_reporting) ||
496 (configure.config.noise_reporting &&
497 !context->caps.noise_reporting) ||
498 (configure.config.cancel_reporting &&
499 !context->caps.cancel_reporting) ||
500 (configure.config.filter_grip &&
501 !context->caps.filter_grip) ||
502 (configure.config.filter_palm &&
Steve Pfetschdf0f0a62022-01-13 19:07:27 +0000503 !context->caps.filter_palm) ||
504 (configure.config.auto_reporting &&
505 !context->caps.auto_reporting)) {
Tai Kuof81f4a12021-02-11 16:22:44 +0800506 pr_err("%s: Invalid configuration enables unsupported features!\n",
507 __func__);
508 err = -EINVAL;
509 return err;
510 } else if (configure.config.sensitivity_setting >=
511 context->caps.num_sensitivity_settings) {
512 pr_err("%s: Invalid configuration enables unsupported sensitivity setting!\n",
513 __func__);
514 err = -EINVAL;
515 return err;
516 } else if ((configure.config.mutual_data_types &
517 ~context->caps.touch_data_types) != 0 ||
518 (configure.config.self_data_types &
519 ~context->caps.touch_data_types) != 0) {
520 pr_err("%s: Invalid configuration enables unsupported data types!\n",
521 __func__);
522 err = -EINVAL;
523 return err;
Steve Pfetschdf0f0a62022-01-13 19:07:27 +0000524 } else if ((configure.config.context_channel_types &
525 ~context->caps.context_channel_types) != 0) {
526 pr_err("%s: Invalid configuration enables unsupported context types!\n",
527 __func__);
528 err = -EINVAL;
529 return err;
Tai Kuof81f4a12021-02-11 16:22:44 +0800530 }
531
532 num_channels = (configure.config.read_coords ? 1 : 0) +
533 hweight_long(
534 configure.config.mutual_data_types) +
535 hweight_long(
Steve Pfetschdf0f0a62022-01-13 19:07:27 +0000536 configure.config.self_data_types) +
537 hweight_long(
538 configure.config.context_channel_types);
Tai Kuof81f4a12021-02-11 16:22:44 +0800539 if (num_channels <= 0 || num_channels > MAX_CHANNELS) {
540 pr_err("%s: Invalid configuration enables more (%d) than %d channels!\n",
541 __func__, num_channels, MAX_CHANNELS);
542 err = -EINVAL;
543 return err;
544 }
545
546 /* Copy sanitized config */
547 memcpy(&context->config, &configure.config,
548 sizeof(context->config));
549
550 /* Allocate frames */
551 err = touch_offload_allocate_buffers(context, NUM_BUFFERS);
552 if (err != 0) {
553 pr_err("%s: failed to allocate buffers. err = 0x%08X.\n",
554 __func__, (unsigned int)err);
555 return err;
556 }
557
558 break;
559 }
560
561 case TOUCH_OFFLOAD_IOC_WR_REPORT:
562 {
563 struct TouchOffloadIocReport report;
564
565 err = copy_from_user(&report, (void *)ioctl_param,
566 sizeof(report));
567 if (err != 0) {
568 pr_err("%s: copy_from_user failed with err=0x%08lx.\n",
569 __func__, err);
570 return err;
571 }
572
573 context->report_cb(context->hcallback, &report);
574 break;
575 }
576
577 default:
578 return -EINVAL;
579 }
580
581 return err;
582}
583
584const struct file_operations touch_offload_fops = {
585 .owner = THIS_MODULE,
586 .open = touch_offload_open,
587 .release = touch_offload_release,
588 .read = touch_offload_read,
589 .poll = touch_offload_poll,
590 .unlocked_ioctl = touch_offload_ioctl
591};
592
593int touch_offload_reserve_frame(struct touch_offload_context *context,
594 struct touch_offload_frame **frame)
595{
596 int ret = 0;
597
598 pr_debug("%s\n", __func__);
599
600 if (!context || !frame)
601 return -EINVAL;
602 *frame = NULL;
603
604 mutex_lock(&context->buffer_lock);
605
606 if (context->num_buffers == 0 || list_empty(&context->free_pool) ||
607 context->reserved_frame != NULL) {
608 pr_debug("%s: buffer not available.\n", __func__);
609 ret = -EINVAL;
610 } else {
611 reinit_completion(&context->reserve_returned);
612 context->reserved_frame =
613 list_entry(context->free_pool.next,
614 struct touch_offload_frame, entry);
615 list_del(&context->reserved_frame->entry);
616 *frame = context->reserved_frame;
617 }
618
619 mutex_unlock(&context->buffer_lock);
620
621 return ret;
622}
623EXPORT_SYMBOL(touch_offload_reserve_frame);
624
625int touch_offload_queue_frame(struct touch_offload_context *context,
626 struct touch_offload_frame *frame)
627{
628 int ret = 0;
629
630 pr_debug("%s\n", __func__);
631
632 if (!context || !frame)
633 return -EINVAL;
634
635 mutex_lock(&context->buffer_lock);
636
637 if (context->num_buffers == 0 || frame != context->reserved_frame) {
638 pr_err("%s: incorrect or NULL buffer submitted.\n", __func__);
639 ret = -EINVAL;
640 } else {
641 /* TODO: Restore important constant fields */
642
643 list_add_tail(&frame->entry, &context->frame_queue);
644 context->reserved_frame = NULL;
645 complete_all(&context->reserve_returned);
646
647 wake_up(&context->read_queue);
648 }
649
650 mutex_unlock(&context->buffer_lock);
651
652 return ret;
653}
654EXPORT_SYMBOL(touch_offload_queue_frame);
655
656int touch_offload_init(struct touch_offload_context *context)
657{
658 int ret = 0;
659
660 pr_debug("%s\n", __func__);
661
662 /* Initialize ioctl interface */
663 context->file_in_use = false;
664 mutex_init(&context->file_lock);
665
666 /* Initialize the buffer pool */
667 context->num_buffers = 0;
668 context->reserved_frame = NULL;
669 INIT_LIST_HEAD(&context->free_pool);
670 INIT_LIST_HEAD(&context->frame_queue);
671 mutex_init(&context->buffer_lock);
672
673 init_waitqueue_head(&context->read_queue);
674
675 init_completion(&context->reserve_returned);
676 complete_all(&context->reserve_returned);
677
Steve Pfetsch5c00f542021-07-27 22:19:48 -0700678 if (!context->multiple_panels)
679 scnprintf(context->device_name, 32, "%s", DEVICE_NAME);
680
Tai Kuof81f4a12021-02-11 16:22:44 +0800681 /* Initialize char device */
Steve Pfetsch5c00f542021-07-27 22:19:48 -0700682 context->major_num = register_chrdev(0, context->device_name,
Tai Kuof81f4a12021-02-11 16:22:44 +0800683 &touch_offload_fops);
684 if (context->major_num < 0) {
685 pr_err("%s: register_chrdev failed with error = %u\n",
686 __func__, context->major_num);
687 return context->major_num;
688 }
689
Steve Pfetsch5c00f542021-07-27 22:19:48 -0700690 context->cls = class_create(THIS_MODULE, context->device_name);
Tai Kuof81f4a12021-02-11 16:22:44 +0800691 if (IS_ERR(context->cls)) {
692 pr_err("%s: class_create failed with error = %ld.\n",
693 __func__, PTR_ERR(context->cls));
Steve Pfetsch5c00f542021-07-27 22:19:48 -0700694 unregister_chrdev(context->major_num, context->device_name);
Tai Kuof81f4a12021-02-11 16:22:44 +0800695 return PTR_ERR(context->cls);
696 }
697
698 context->device = device_create(context->cls, NULL,
699 MKDEV(context->major_num, 0), NULL,
Steve Pfetsch5c00f542021-07-27 22:19:48 -0700700 context->device_name);
Tai Kuof81f4a12021-02-11 16:22:44 +0800701 if (IS_ERR(context->device)) {
702 pr_err("%s: device_create failed with error = %ld.\n",
703 __func__, PTR_ERR(context->device));
704 class_destroy(context->cls);
Steve Pfetsch5c00f542021-07-27 22:19:48 -0700705 unregister_chrdev(context->major_num, context->device_name);
Tai Kuof81f4a12021-02-11 16:22:44 +0800706 return PTR_ERR(context->device);
707 }
708
709 cdev_init(&context->dev, &touch_offload_fops);
710 cdev_add(&context->dev, MKDEV(context->major_num, 0), 1);
711
712 return ret;
713}
714EXPORT_SYMBOL(touch_offload_init);
715
716int touch_offload_cleanup(struct touch_offload_context *context)
717{
718 pr_debug("%s\n", __func__);
719
720 cdev_del(&context->dev);
721
722 device_destroy(context->cls, MKDEV(context->major_num, 0));
723
724 class_destroy(context->cls);
725
Steve Pfetsch5c00f542021-07-27 22:19:48 -0700726 unregister_chrdev(context->major_num, context->device_name);
Tai Kuof81f4a12021-02-11 16:22:44 +0800727
davidycchen44b9f142022-04-21 15:15:03 +0800728 complete_all(&context->reserve_returned);
729
Tai Kuof81f4a12021-02-11 16:22:44 +0800730 touch_offload_free_buffers(context);
731
732 return 0;
733}
734EXPORT_SYMBOL(touch_offload_cleanup);
735
736MODULE_DESCRIPTION("Touch Offload to AP");
737MODULE_AUTHOR("Steve Pfetsch <spfetsch@google.com>");
738MODULE_LICENSE("GPL v2");