2 * Userspace driver for the LED subsystem
4 * Copyright (C) 2016 David Lechner <david@lechnology.com>
6 * Based on uinput.c: Aristeu Sergio Rozanski Filho <aris@cathedrallabs.org>
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
19 #include <linux/init.h>
20 #include <linux/leds.h>
21 #include <linux/miscdevice.h>
22 #include <linux/module.h>
23 #include <linux/poll.h>
24 #include <linux/sched.h>
25 #include <linux/slab.h>
27 #include <uapi/linux/uleds.h>
29 #define ULEDS_NAME "uleds"
33 ULEDS_STATE_REGISTERED
,
37 struct uleds_user_dev user_dev
;
38 struct led_classdev led_cdev
;
40 enum uleds_state state
;
41 wait_queue_head_t waitq
;
42 unsigned char brightness
;
43 unsigned char new_data
;
46 static void uleds_brightness_set(struct led_classdev
*led_cdev
,
47 enum led_brightness brightness
)
49 struct uleds_device
*udev
= container_of(led_cdev
, struct uleds_device
,
52 if (udev
->brightness
!= brightness
) {
53 udev
->brightness
= brightness
;
55 wake_up_interruptible(&udev
->waitq
);
59 static int uleds_open(struct inode
*inode
, struct file
*file
)
61 struct uleds_device
*udev
;
63 udev
= kzalloc(sizeof(*udev
), GFP_KERNEL
);
67 udev
->led_cdev
.name
= udev
->user_dev
.name
;
68 udev
->led_cdev
.max_brightness
= LED_FULL
;
69 udev
->led_cdev
.brightness_set
= uleds_brightness_set
;
71 mutex_init(&udev
->mutex
);
72 init_waitqueue_head(&udev
->waitq
);
73 udev
->state
= ULEDS_STATE_UNKNOWN
;
75 file
->private_data
= udev
;
76 nonseekable_open(inode
, file
);
81 static ssize_t
uleds_write(struct file
*file
, const char __user
*buffer
,
82 size_t count
, loff_t
*ppos
)
84 struct uleds_device
*udev
= file
->private_data
;
90 ret
= mutex_lock_interruptible(&udev
->mutex
);
94 if (udev
->state
== ULEDS_STATE_REGISTERED
) {
99 if (count
!= sizeof(struct uleds_user_dev
)) {
104 if (copy_from_user(&udev
->user_dev
, buffer
,
105 sizeof(struct uleds_user_dev
))) {
110 if (!udev
->user_dev
.name
[0]) {
115 ret
= led_classdev_register(NULL
, &udev
->led_cdev
);
119 udev
->state
= ULEDS_STATE_REGISTERED
;
123 mutex_unlock(&udev
->mutex
);
128 static ssize_t
uleds_read(struct file
*file
, char __user
*buffer
, size_t count
,
131 struct uleds_device
*udev
= file
->private_data
;
141 retval
= mutex_lock_interruptible(&udev
->mutex
);
145 if (udev
->state
!= ULEDS_STATE_REGISTERED
) {
147 } else if (!udev
->new_data
&& (file
->f_flags
& O_NONBLOCK
)) {
150 retval
= copy_to_user(buffer
, &udev
->brightness
, 1);
155 mutex_unlock(&udev
->mutex
);
157 if (retval
|| count
== 0)
160 if (!(file
->f_flags
& O_NONBLOCK
))
161 retval
= wait_event_interruptible(udev
->waitq
,
163 udev
->state
!= ULEDS_STATE_REGISTERED
);
164 } while (retval
== 0);
169 static unsigned int uleds_poll(struct file
*file
, poll_table
*wait
)
171 struct uleds_device
*udev
= file
->private_data
;
173 poll_wait(file
, &udev
->waitq
, wait
);
176 return POLLIN
| POLLRDNORM
;
181 static int uleds_release(struct inode
*inode
, struct file
*file
)
183 struct uleds_device
*udev
= file
->private_data
;
185 if (udev
->state
== ULEDS_STATE_REGISTERED
) {
186 udev
->state
= ULEDS_STATE_UNKNOWN
;
187 led_classdev_unregister(&udev
->led_cdev
);
194 static const struct file_operations uleds_fops
= {
195 .owner
= THIS_MODULE
,
197 .release
= uleds_release
,
199 .write
= uleds_write
,
204 static struct miscdevice uleds_misc
= {
206 .minor
= MISC_DYNAMIC_MINOR
,
210 static int __init
uleds_init(void)
212 return misc_register(&uleds_misc
);
214 module_init(uleds_init
);
216 static void __exit
uleds_exit(void)
218 misc_deregister(&uleds_misc
);
220 module_exit(uleds_exit
);
222 MODULE_AUTHOR("David Lechner <david@lechnology.com>");
223 MODULE_DESCRIPTION("Userspace driver for the LED subsystem");
224 MODULE_LICENSE("GPL");