1 // SPDX-License-Identifier: GPL-2.0
5 * Copyright (C) 2018 Johan Hovold <johan@kernel.org>
8 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
10 #include <linux/cdev.h>
11 #include <linux/errno.h>
13 #include <linux/gnss.h>
14 #include <linux/idr.h>
15 #include <linux/init.h>
16 #include <linux/kernel.h>
17 #include <linux/module.h>
18 #include <linux/poll.h>
19 #include <linux/slab.h>
20 #include <linux/uaccess.h>
21 #include <linux/wait.h>
23 #define GNSS_FLAG_HAS_WRITE_RAW BIT(0)
25 #define GNSS_MINORS 16
27 static DEFINE_IDA(gnss_minors
);
28 static dev_t gnss_first
;
30 /* FIFO size must be a power of two */
31 #define GNSS_READ_FIFO_SIZE 4096
32 #define GNSS_WRITE_BUF_SIZE 1024
34 #define to_gnss_device(d) container_of((d), struct gnss_device, dev)
36 static int gnss_open(struct inode
*inode
, struct file
*file
)
38 struct gnss_device
*gdev
;
41 gdev
= container_of(inode
->i_cdev
, struct gnss_device
, cdev
);
43 get_device(&gdev
->dev
);
45 stream_open(inode
, file
);
46 file
->private_data
= gdev
;
48 down_write(&gdev
->rwsem
);
49 if (gdev
->disconnected
) {
54 if (gdev
->count
++ == 0) {
55 ret
= gdev
->ops
->open(gdev
);
60 up_write(&gdev
->rwsem
);
63 put_device(&gdev
->dev
);
68 static int gnss_release(struct inode
*inode
, struct file
*file
)
70 struct gnss_device
*gdev
= file
->private_data
;
72 down_write(&gdev
->rwsem
);
73 if (gdev
->disconnected
)
76 if (--gdev
->count
== 0) {
77 gdev
->ops
->close(gdev
);
78 kfifo_reset(&gdev
->read_fifo
);
81 up_write(&gdev
->rwsem
);
83 put_device(&gdev
->dev
);
88 static ssize_t
gnss_read(struct file
*file
, char __user
*buf
,
89 size_t count
, loff_t
*pos
)
91 struct gnss_device
*gdev
= file
->private_data
;
95 mutex_lock(&gdev
->read_mutex
);
96 while (kfifo_is_empty(&gdev
->read_fifo
)) {
97 mutex_unlock(&gdev
->read_mutex
);
99 if (gdev
->disconnected
)
102 if (file
->f_flags
& O_NONBLOCK
)
105 ret
= wait_event_interruptible(gdev
->read_queue
,
106 gdev
->disconnected
||
107 !kfifo_is_empty(&gdev
->read_fifo
));
111 mutex_lock(&gdev
->read_mutex
);
114 ret
= kfifo_to_user(&gdev
->read_fifo
, buf
, count
, &copied
);
118 mutex_unlock(&gdev
->read_mutex
);
123 static ssize_t
gnss_write(struct file
*file
, const char __user
*buf
,
124 size_t count
, loff_t
*pos
)
126 struct gnss_device
*gdev
= file
->private_data
;
130 if (gdev
->disconnected
)
136 if (!(gdev
->flags
& GNSS_FLAG_HAS_WRITE_RAW
))
139 /* Ignoring O_NONBLOCK, write_raw() is synchronous. */
141 ret
= mutex_lock_interruptible(&gdev
->write_mutex
);
146 size_t n
= count
- written
;
148 if (n
> GNSS_WRITE_BUF_SIZE
)
149 n
= GNSS_WRITE_BUF_SIZE
;
151 if (copy_from_user(gdev
->write_buf
, buf
, n
)) {
157 * Assumes write_raw can always accept GNSS_WRITE_BUF_SIZE
162 down_read(&gdev
->rwsem
);
163 if (!gdev
->disconnected
)
164 ret
= gdev
->ops
->write_raw(gdev
, gdev
->write_buf
, n
);
167 up_read(&gdev
->rwsem
);
175 if (written
== count
)
182 mutex_unlock(&gdev
->write_mutex
);
187 static __poll_t
gnss_poll(struct file
*file
, poll_table
*wait
)
189 struct gnss_device
*gdev
= file
->private_data
;
192 poll_wait(file
, &gdev
->read_queue
, wait
);
194 if (!kfifo_is_empty(&gdev
->read_fifo
))
195 mask
|= EPOLLIN
| EPOLLRDNORM
;
196 if (gdev
->disconnected
)
202 static const struct file_operations gnss_fops
= {
203 .owner
= THIS_MODULE
,
205 .release
= gnss_release
,
212 static struct class *gnss_class
;
214 static void gnss_device_release(struct device
*dev
)
216 struct gnss_device
*gdev
= to_gnss_device(dev
);
218 kfree(gdev
->write_buf
);
219 kfifo_free(&gdev
->read_fifo
);
220 ida_simple_remove(&gnss_minors
, gdev
->id
);
224 struct gnss_device
*gnss_allocate_device(struct device
*parent
)
226 struct gnss_device
*gdev
;
231 gdev
= kzalloc(sizeof(*gdev
), GFP_KERNEL
);
235 id
= ida_simple_get(&gnss_minors
, 0, GNSS_MINORS
, GFP_KERNEL
);
244 device_initialize(dev
);
245 dev
->devt
= gnss_first
+ id
;
246 dev
->class = gnss_class
;
247 dev
->parent
= parent
;
248 dev
->release
= gnss_device_release
;
249 dev_set_drvdata(dev
, gdev
);
250 dev_set_name(dev
, "gnss%d", id
);
252 init_rwsem(&gdev
->rwsem
);
253 mutex_init(&gdev
->read_mutex
);
254 mutex_init(&gdev
->write_mutex
);
255 init_waitqueue_head(&gdev
->read_queue
);
257 ret
= kfifo_alloc(&gdev
->read_fifo
, GNSS_READ_FIFO_SIZE
, GFP_KERNEL
);
261 gdev
->write_buf
= kzalloc(GNSS_WRITE_BUF_SIZE
, GFP_KERNEL
);
262 if (!gdev
->write_buf
)
265 cdev_init(&gdev
->cdev
, &gnss_fops
);
266 gdev
->cdev
.owner
= THIS_MODULE
;
275 EXPORT_SYMBOL_GPL(gnss_allocate_device
);
277 void gnss_put_device(struct gnss_device
*gdev
)
279 put_device(&gdev
->dev
);
281 EXPORT_SYMBOL_GPL(gnss_put_device
);
283 int gnss_register_device(struct gnss_device
*gdev
)
287 /* Set a flag which can be accessed without holding the rwsem. */
288 if (gdev
->ops
->write_raw
!= NULL
)
289 gdev
->flags
|= GNSS_FLAG_HAS_WRITE_RAW
;
291 ret
= cdev_device_add(&gdev
->cdev
, &gdev
->dev
);
293 dev_err(&gdev
->dev
, "failed to add device: %d\n", ret
);
299 EXPORT_SYMBOL_GPL(gnss_register_device
);
301 void gnss_deregister_device(struct gnss_device
*gdev
)
303 down_write(&gdev
->rwsem
);
304 gdev
->disconnected
= true;
306 wake_up_interruptible(&gdev
->read_queue
);
307 gdev
->ops
->close(gdev
);
309 up_write(&gdev
->rwsem
);
311 cdev_device_del(&gdev
->cdev
, &gdev
->dev
);
313 EXPORT_SYMBOL_GPL(gnss_deregister_device
);
316 * Caller guarantees serialisation.
318 * Must not be called for a closed device.
320 int gnss_insert_raw(struct gnss_device
*gdev
, const unsigned char *buf
,
325 ret
= kfifo_in(&gdev
->read_fifo
, buf
, count
);
327 wake_up_interruptible(&gdev
->read_queue
);
331 EXPORT_SYMBOL_GPL(gnss_insert_raw
);
333 static const char * const gnss_type_names
[GNSS_TYPE_COUNT
] = {
334 [GNSS_TYPE_NMEA
] = "NMEA",
335 [GNSS_TYPE_SIRF
] = "SiRF",
336 [GNSS_TYPE_UBX
] = "UBX",
337 [GNSS_TYPE_MTK
] = "MTK",
340 static const char *gnss_type_name(struct gnss_device
*gdev
)
342 const char *name
= NULL
;
344 if (gdev
->type
< GNSS_TYPE_COUNT
)
345 name
= gnss_type_names
[gdev
->type
];
348 dev_WARN(&gdev
->dev
, "type name not defined\n");
353 static ssize_t
type_show(struct device
*dev
, struct device_attribute
*attr
,
356 struct gnss_device
*gdev
= to_gnss_device(dev
);
358 return sprintf(buf
, "%s\n", gnss_type_name(gdev
));
360 static DEVICE_ATTR_RO(type
);
362 static struct attribute
*gnss_attrs
[] = {
366 ATTRIBUTE_GROUPS(gnss
);
368 static int gnss_uevent(struct device
*dev
, struct kobj_uevent_env
*env
)
370 struct gnss_device
*gdev
= to_gnss_device(dev
);
373 ret
= add_uevent_var(env
, "GNSS_TYPE=%s", gnss_type_name(gdev
));
380 static int __init
gnss_module_init(void)
384 ret
= alloc_chrdev_region(&gnss_first
, 0, GNSS_MINORS
, "gnss");
386 pr_err("failed to allocate device numbers: %d\n", ret
);
390 gnss_class
= class_create(THIS_MODULE
, "gnss");
391 if (IS_ERR(gnss_class
)) {
392 ret
= PTR_ERR(gnss_class
);
393 pr_err("failed to create class: %d\n", ret
);
394 goto err_unregister_chrdev
;
397 gnss_class
->dev_groups
= gnss_groups
;
398 gnss_class
->dev_uevent
= gnss_uevent
;
400 pr_info("GNSS driver registered with major %d\n", MAJOR(gnss_first
));
404 err_unregister_chrdev
:
405 unregister_chrdev_region(gnss_first
, GNSS_MINORS
);
409 module_init(gnss_module_init
);
411 static void __exit
gnss_module_exit(void)
413 class_destroy(gnss_class
);
414 unregister_chrdev_region(gnss_first
, GNSS_MINORS
);
415 ida_destroy(&gnss_minors
);
417 module_exit(gnss_module_exit
);
419 MODULE_AUTHOR("Johan Hovold <johan@kernel.org>");
420 MODULE_DESCRIPTION("GNSS receiver core");
421 MODULE_LICENSE("GPL v2");