Lines Matching +full:client +full:- +full:id

1 // SPDX-License-Identifier: GPL-2.0+
3 * Provides user-space access to the SSAM EC via the /dev/surface/aggregator
6 * Copyright (C) 2020-2022 Maximilian Luz <luzmaximilian@gmail.com>
30 /* -- Main structures. ------------------------------------------------------ */
45 struct rw_semaphore client_lock; /* Guards client list. */
52 struct ssam_cdev_client *client; member
79 kref_get(&cdev->kref); in ssam_cdev_get()
87 kref_put(&cdev->kref, __ssam_cdev_release); in ssam_cdev_put()
91 /* -- Notifier handling. ---------------------------------------------------- */
96 struct ssam_cdev_client *client = cdev_nf->client; in ssam_cdev_notifier() local
98 size_t n = struct_size(&event, data, in->length); in ssam_cdev_notifier()
101 event.target_category = in->target_category; in ssam_cdev_notifier()
102 event.target_id = in->target_id; in ssam_cdev_notifier()
103 event.command_id = in->command_id; in ssam_cdev_notifier()
104 event.instance_id = in->instance_id; in ssam_cdev_notifier()
105 event.length = in->length; in ssam_cdev_notifier()
107 mutex_lock(&client->write_lock); in ssam_cdev_notifier()
110 if (kfifo_avail(&client->buffer) < n) { in ssam_cdev_notifier()
111 dev_warn(client->cdev->dev, in ssam_cdev_notifier()
113 in->target_category, in->target_id, in->command_id, in->instance_id); in ssam_cdev_notifier()
114 mutex_unlock(&client->write_lock); in ssam_cdev_notifier()
119 kfifo_in(&client->buffer, (const u8 *)&event, struct_size(&event, data, 0)); in ssam_cdev_notifier()
120 kfifo_in(&client->buffer, &in->data[0], in->length); in ssam_cdev_notifier()
122 mutex_unlock(&client->write_lock); in ssam_cdev_notifier()
125 kill_fasync(&client->fasync, SIGIO, POLL_IN); in ssam_cdev_notifier()
126 wake_up_interruptible(&client->waitq); in ssam_cdev_notifier()
135 static int ssam_cdev_notifier_register(struct ssam_cdev_client *client, u8 tc, int priority) in ssam_cdev_notifier_register() argument
142 lockdep_assert_held_read(&client->cdev->lock); in ssam_cdev_notifier_register()
146 return -EINVAL; in ssam_cdev_notifier_register()
148 mutex_lock(&client->notifier_lock); in ssam_cdev_notifier_register()
151 if (client->notifier[event]) { in ssam_cdev_notifier_register()
152 mutex_unlock(&client->notifier_lock); in ssam_cdev_notifier_register()
153 return -EEXIST; in ssam_cdev_notifier_register()
159 mutex_unlock(&client->notifier_lock); in ssam_cdev_notifier_register()
160 return -ENOMEM; in ssam_cdev_notifier_register()
169 nf->client = client; in ssam_cdev_notifier_register()
170 nf->nf.base.fn = ssam_cdev_notifier; in ssam_cdev_notifier_register()
171 nf->nf.base.priority = priority; in ssam_cdev_notifier_register()
172 nf->nf.event.id.target_category = tc; in ssam_cdev_notifier_register()
173 nf->nf.event.mask = 0; /* Do not do any matching. */ in ssam_cdev_notifier_register()
174 nf->nf.flags = SSAM_EVENT_NOTIFIER_OBSERVER; in ssam_cdev_notifier_register()
177 status = ssam_notifier_register(client->cdev->ctrl, &nf->nf); in ssam_cdev_notifier_register()
181 client->notifier[event] = nf; in ssam_cdev_notifier_register()
183 mutex_unlock(&client->notifier_lock); in ssam_cdev_notifier_register()
187 static int ssam_cdev_notifier_unregister(struct ssam_cdev_client *client, u8 tc) in ssam_cdev_notifier_unregister() argument
193 lockdep_assert_held_read(&client->cdev->lock); in ssam_cdev_notifier_unregister()
197 return -EINVAL; in ssam_cdev_notifier_unregister()
199 mutex_lock(&client->notifier_lock); in ssam_cdev_notifier_unregister()
202 if (!client->notifier[event]) { in ssam_cdev_notifier_unregister()
203 mutex_unlock(&client->notifier_lock); in ssam_cdev_notifier_unregister()
204 return -ENOENT; in ssam_cdev_notifier_unregister()
208 status = ssam_notifier_unregister(client->cdev->ctrl, &client->notifier[event]->nf); in ssam_cdev_notifier_unregister()
209 kfree(client->notifier[event]); in ssam_cdev_notifier_unregister()
210 client->notifier[event] = NULL; in ssam_cdev_notifier_unregister()
212 mutex_unlock(&client->notifier_lock); in ssam_cdev_notifier_unregister()
216 static void ssam_cdev_notifier_unregister_all(struct ssam_cdev_client *client) in ssam_cdev_notifier_unregister_all() argument
220 down_read(&client->cdev->lock); in ssam_cdev_notifier_unregister_all()
224 * cdev->ctrl instead of the SSAM_CDEV_DEVICE_SHUTDOWN_BIT bit. in ssam_cdev_notifier_unregister_all()
226 if (client->cdev->ctrl) { in ssam_cdev_notifier_unregister_all()
228 ssam_cdev_notifier_unregister(client, i + 1); in ssam_cdev_notifier_unregister_all()
238 mutex_lock(&client->notifier_lock); in ssam_cdev_notifier_unregister_all()
240 count += !!(client->notifier[i]); in ssam_cdev_notifier_unregister_all()
241 kfree(client->notifier[i]); in ssam_cdev_notifier_unregister_all()
242 client->notifier[i] = NULL; in ssam_cdev_notifier_unregister_all()
244 mutex_unlock(&client->notifier_lock); in ssam_cdev_notifier_unregister_all()
249 up_read(&client->cdev->lock); in ssam_cdev_notifier_unregister_all()
253 /* -- IOCTL functions. ------------------------------------------------------ */
255 static long ssam_cdev_request(struct ssam_cdev_client *client, struct ssam_cdev_request __user *r) in ssam_cdev_request() argument
264 lockdep_assert_held_read(&client->cdev->lock); in ssam_cdev_request()
292 /* Get request payload from user-space. */ in ssam_cdev_request()
295 ret = -EINVAL; in ssam_cdev_request()
310 ret = -ENOMEM; in ssam_cdev_request()
315 ret = -EFAULT; in ssam_cdev_request()
323 ret = -EINVAL; in ssam_cdev_request()
339 ret = -ENOMEM; in ssam_cdev_request()
345 status = ssam_request_do_sync(client->cdev->ctrl, &spec, &rsp); in ssam_cdev_request()
349 /* Copy response to user-space. */ in ssam_cdev_request()
351 ret = -EFAULT; in ssam_cdev_request()
354 /* Always try to set response-length and status. */ in ssam_cdev_request()
355 tmp = put_user(rsp.length, &r->response.length); in ssam_cdev_request()
359 tmp = put_user(status, &r->status); in ssam_cdev_request()
370 static long ssam_cdev_notif_register(struct ssam_cdev_client *client, in ssam_cdev_notif_register() argument
376 lockdep_assert_held_read(&client->cdev->lock); in ssam_cdev_notif_register()
382 return ssam_cdev_notifier_register(client, desc.target_category, desc.priority); in ssam_cdev_notif_register()
385 static long ssam_cdev_notif_unregister(struct ssam_cdev_client *client, in ssam_cdev_notif_unregister() argument
391 lockdep_assert_held_read(&client->cdev->lock); in ssam_cdev_notif_unregister()
397 return ssam_cdev_notifier_unregister(client, desc.target_category); in ssam_cdev_notif_unregister()
400 static long ssam_cdev_event_enable(struct ssam_cdev_client *client, in ssam_cdev_event_enable() argument
405 struct ssam_event_id id; in ssam_cdev_event_enable() local
408 lockdep_assert_held_read(&client->cdev->lock); in ssam_cdev_event_enable()
410 /* Read descriptor from user-space. */ in ssam_cdev_event_enable()
421 id.target_category = desc.id.target_category; in ssam_cdev_event_enable()
422 id.instance = desc.id.instance; in ssam_cdev_event_enable()
425 return ssam_controller_event_enable(client->cdev->ctrl, reg, id, desc.flags); in ssam_cdev_event_enable()
428 static long ssam_cdev_event_disable(struct ssam_cdev_client *client, in ssam_cdev_event_disable() argument
433 struct ssam_event_id id; in ssam_cdev_event_disable() local
436 lockdep_assert_held_read(&client->cdev->lock); in ssam_cdev_event_disable()
438 /* Read descriptor from user-space. */ in ssam_cdev_event_disable()
449 id.target_category = desc.id.target_category; in ssam_cdev_event_disable()
450 id.instance = desc.id.instance; in ssam_cdev_event_disable()
453 return ssam_controller_event_disable(client->cdev->ctrl, reg, id, desc.flags); in ssam_cdev_event_disable()
457 /* -- File operations. ------------------------------------------------------ */
461 struct miscdevice *mdev = filp->private_data; in ssam_cdev_device_open()
462 struct ssam_cdev_client *client; in ssam_cdev_device_open() local
465 /* Initialize client */ in ssam_cdev_device_open()
466 client = vzalloc(sizeof(*client)); in ssam_cdev_device_open()
467 if (!client) in ssam_cdev_device_open()
468 return -ENOMEM; in ssam_cdev_device_open()
470 client->cdev = ssam_cdev_get(cdev); in ssam_cdev_device_open()
472 INIT_LIST_HEAD(&client->node); in ssam_cdev_device_open()
474 mutex_init(&client->notifier_lock); in ssam_cdev_device_open()
476 mutex_init(&client->read_lock); in ssam_cdev_device_open()
477 mutex_init(&client->write_lock); in ssam_cdev_device_open()
478 INIT_KFIFO(client->buffer); in ssam_cdev_device_open()
479 init_waitqueue_head(&client->waitq); in ssam_cdev_device_open()
481 filp->private_data = client; in ssam_cdev_device_open()
483 /* Attach client. */ in ssam_cdev_device_open()
484 down_write(&cdev->client_lock); in ssam_cdev_device_open()
486 if (test_bit(SSAM_CDEV_DEVICE_SHUTDOWN_BIT, &cdev->flags)) { in ssam_cdev_device_open()
487 up_write(&cdev->client_lock); in ssam_cdev_device_open()
488 mutex_destroy(&client->write_lock); in ssam_cdev_device_open()
489 mutex_destroy(&client->read_lock); in ssam_cdev_device_open()
490 mutex_destroy(&client->notifier_lock); in ssam_cdev_device_open()
491 ssam_cdev_put(client->cdev); in ssam_cdev_device_open()
492 vfree(client); in ssam_cdev_device_open()
493 return -ENODEV; in ssam_cdev_device_open()
495 list_add_tail(&client->node, &cdev->client_list); in ssam_cdev_device_open()
497 up_write(&cdev->client_lock); in ssam_cdev_device_open()
505 struct ssam_cdev_client *client = filp->private_data; in ssam_cdev_device_release() local
507 /* Force-unregister all remaining notifiers of this client. */ in ssam_cdev_device_release()
508 ssam_cdev_notifier_unregister_all(client); in ssam_cdev_device_release()
510 /* Detach client. */ in ssam_cdev_device_release()
511 down_write(&client->cdev->client_lock); in ssam_cdev_device_release()
512 list_del(&client->node); in ssam_cdev_device_release()
513 up_write(&client->cdev->client_lock); in ssam_cdev_device_release()
515 /* Free client. */ in ssam_cdev_device_release()
516 mutex_destroy(&client->write_lock); in ssam_cdev_device_release()
517 mutex_destroy(&client->read_lock); in ssam_cdev_device_release()
519 mutex_destroy(&client->notifier_lock); in ssam_cdev_device_release()
521 ssam_cdev_put(client->cdev); in ssam_cdev_device_release()
522 vfree(client); in ssam_cdev_device_release()
527 static long __ssam_cdev_device_ioctl(struct ssam_cdev_client *client, unsigned int cmd, in __ssam_cdev_device_ioctl() argument
530 lockdep_assert_held_read(&client->cdev->lock); in __ssam_cdev_device_ioctl()
534 return ssam_cdev_request(client, (struct ssam_cdev_request __user *)arg); in __ssam_cdev_device_ioctl()
537 return ssam_cdev_notif_register(client, in __ssam_cdev_device_ioctl()
541 return ssam_cdev_notif_unregister(client, in __ssam_cdev_device_ioctl()
545 return ssam_cdev_event_enable(client, (struct ssam_cdev_event_desc __user *)arg); in __ssam_cdev_device_ioctl()
548 return ssam_cdev_event_disable(client, (struct ssam_cdev_event_desc __user *)arg); in __ssam_cdev_device_ioctl()
551 return -ENOTTY; in __ssam_cdev_device_ioctl()
557 struct ssam_cdev_client *client = file->private_data; in ssam_cdev_device_ioctl() local
561 if (down_read_killable(&client->cdev->lock)) in ssam_cdev_device_ioctl()
562 return -ERESTARTSYS; in ssam_cdev_device_ioctl()
564 if (test_bit(SSAM_CDEV_DEVICE_SHUTDOWN_BIT, &client->cdev->flags)) { in ssam_cdev_device_ioctl()
565 up_read(&client->cdev->lock); in ssam_cdev_device_ioctl()
566 return -ENODEV; in ssam_cdev_device_ioctl()
569 status = __ssam_cdev_device_ioctl(client, cmd, arg); in ssam_cdev_device_ioctl()
571 up_read(&client->cdev->lock); in ssam_cdev_device_ioctl()
577 struct ssam_cdev_client *client = file->private_data; in ssam_cdev_read() local
578 struct ssam_cdev *cdev = client->cdev; in ssam_cdev_read()
582 if (down_read_killable(&cdev->lock)) in ssam_cdev_read()
583 return -ERESTARTSYS; in ssam_cdev_read()
586 if (test_bit(SSAM_CDEV_DEVICE_SHUTDOWN_BIT, &cdev->flags)) { in ssam_cdev_read()
587 up_read(&cdev->lock); in ssam_cdev_read()
588 return -ENODEV; in ssam_cdev_read()
593 if (kfifo_is_empty(&client->buffer)) { in ssam_cdev_read()
594 up_read(&cdev->lock); in ssam_cdev_read()
596 if (file->f_flags & O_NONBLOCK) in ssam_cdev_read()
597 return -EAGAIN; in ssam_cdev_read()
599 status = wait_event_interruptible(client->waitq, in ssam_cdev_read()
600 !kfifo_is_empty(&client->buffer) || in ssam_cdev_read()
602 &cdev->flags)); in ssam_cdev_read()
606 if (down_read_killable(&cdev->lock)) in ssam_cdev_read()
607 return -ERESTARTSYS; in ssam_cdev_read()
610 if (test_bit(SSAM_CDEV_DEVICE_SHUTDOWN_BIT, &cdev->flags)) { in ssam_cdev_read()
611 up_read(&cdev->lock); in ssam_cdev_read()
612 return -ENODEV; in ssam_cdev_read()
617 if (mutex_lock_interruptible(&client->read_lock)) { in ssam_cdev_read()
618 up_read(&cdev->lock); in ssam_cdev_read()
619 return -ERESTARTSYS; in ssam_cdev_read()
622 status = kfifo_to_user(&client->buffer, buf, count, &copied); in ssam_cdev_read()
623 mutex_unlock(&client->read_lock); in ssam_cdev_read()
626 up_read(&cdev->lock); in ssam_cdev_read()
631 if (copied == 0 && (file->f_flags & O_NONBLOCK)) { in ssam_cdev_read()
632 up_read(&cdev->lock); in ssam_cdev_read()
633 return -EAGAIN; in ssam_cdev_read()
637 up_read(&cdev->lock); in ssam_cdev_read()
643 struct ssam_cdev_client *client = file->private_data; in ssam_cdev_poll() local
646 if (test_bit(SSAM_CDEV_DEVICE_SHUTDOWN_BIT, &client->cdev->flags)) in ssam_cdev_poll()
649 poll_wait(file, &client->waitq, pt); in ssam_cdev_poll()
651 if (!kfifo_is_empty(&client->buffer)) in ssam_cdev_poll()
659 struct ssam_cdev_client *client = file->private_data; in ssam_cdev_fasync() local
661 return fasync_helper(fd, file, on, &client->fasync); in ssam_cdev_fasync()
676 /* -- Device and driver setup ----------------------------------------------- */
684 ctrl = ssam_client_bind(&pdev->dev); in ssam_dbg_device_probe()
686 return PTR_ERR(ctrl) == -ENODEV ? -EPROBE_DEFER : PTR_ERR(ctrl); in ssam_dbg_device_probe()
690 return -ENOMEM; in ssam_dbg_device_probe()
692 kref_init(&cdev->kref); in ssam_dbg_device_probe()
693 init_rwsem(&cdev->lock); in ssam_dbg_device_probe()
694 cdev->ctrl = ctrl; in ssam_dbg_device_probe()
695 cdev->dev = &pdev->dev; in ssam_dbg_device_probe()
697 cdev->mdev.parent = &pdev->dev; in ssam_dbg_device_probe()
698 cdev->mdev.minor = MISC_DYNAMIC_MINOR; in ssam_dbg_device_probe()
699 cdev->mdev.name = "surface_aggregator"; in ssam_dbg_device_probe()
700 cdev->mdev.nodename = "surface/aggregator"; in ssam_dbg_device_probe()
701 cdev->mdev.fops = &ssam_controller_fops; in ssam_dbg_device_probe()
703 init_rwsem(&cdev->client_lock); in ssam_dbg_device_probe()
704 INIT_LIST_HEAD(&cdev->client_list); in ssam_dbg_device_probe()
706 status = misc_register(&cdev->mdev); in ssam_dbg_device_probe()
719 struct ssam_cdev_client *client; in ssam_dbg_device_remove() local
722 * Mark device as shut-down. Prevent new clients from being added and in ssam_dbg_device_remove()
725 set_bit(SSAM_CDEV_DEVICE_SHUTDOWN_BIT, &cdev->flags); in ssam_dbg_device_remove()
727 down_write(&cdev->client_lock); in ssam_dbg_device_remove()
730 list_for_each_entry(client, &cdev->client_list, node) { in ssam_dbg_device_remove()
731 ssam_cdev_notifier_unregister_all(client); in ssam_dbg_device_remove()
735 list_for_each_entry(client, &cdev->client_list, node) { in ssam_dbg_device_remove()
736 kill_fasync(&client->fasync, SIGIO, POLL_HUP); in ssam_dbg_device_remove()
740 list_for_each_entry(client, &cdev->client_list, node) { in ssam_dbg_device_remove()
741 wake_up_interruptible(&client->waitq); in ssam_dbg_device_remove()
744 up_write(&cdev->client_lock); in ssam_dbg_device_remove()
751 down_write(&cdev->lock); in ssam_dbg_device_remove()
752 cdev->ctrl = NULL; in ssam_dbg_device_remove()
753 cdev->dev = NULL; in ssam_dbg_device_remove()
754 up_write(&cdev->lock); in ssam_dbg_device_remove()
756 misc_deregister(&cdev->mdev); in ssam_dbg_device_remove()
779 return -ENOMEM; in ssam_debug_init()
807 MODULE_DESCRIPTION("User-space interface for Surface System Aggregator Module");