1 | // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause |
2 | /* |
3 | * Copyright(c) 2015, 2016 Intel Corporation. |
4 | */ |
5 | |
6 | #include <linux/cdev.h> |
7 | #include <linux/device.h> |
8 | #include <linux/fs.h> |
9 | |
10 | #include "hfi.h" |
11 | #include "device.h" |
12 | |
13 | static char *hfi1_devnode(const struct device *dev, umode_t *mode) |
14 | { |
15 | if (mode) |
16 | *mode = 0600; |
17 | return kasprintf(GFP_KERNEL, fmt: "%s" , dev_name(dev)); |
18 | } |
19 | |
20 | static const struct class class = { |
21 | .name = "hfi1" , |
22 | .devnode = hfi1_devnode, |
23 | }; |
24 | |
25 | static char *hfi1_user_devnode(const struct device *dev, umode_t *mode) |
26 | { |
27 | if (mode) |
28 | *mode = 0666; |
29 | return kasprintf(GFP_KERNEL, fmt: "%s" , dev_name(dev)); |
30 | } |
31 | |
32 | static const struct class user_class = { |
33 | .name = "hfi1_user" , |
34 | .devnode = hfi1_user_devnode, |
35 | }; |
36 | static dev_t hfi1_dev; |
37 | |
38 | int hfi1_cdev_init(int minor, const char *name, |
39 | const struct file_operations *fops, |
40 | struct cdev *cdev, struct device **devp, |
41 | bool user_accessible, |
42 | struct kobject *parent) |
43 | { |
44 | const dev_t dev = MKDEV(MAJOR(hfi1_dev), minor); |
45 | struct device *device = NULL; |
46 | int ret; |
47 | |
48 | cdev_init(cdev, fops); |
49 | cdev->owner = THIS_MODULE; |
50 | cdev_set_parent(p: cdev, kobj: parent); |
51 | kobject_set_name(kobj: &cdev->kobj, name); |
52 | |
53 | ret = cdev_add(cdev, dev, 1); |
54 | if (ret < 0) { |
55 | pr_err("Could not add cdev for minor %d, %s (err %d)\n" , |
56 | minor, name, -ret); |
57 | goto done; |
58 | } |
59 | |
60 | if (user_accessible) |
61 | device = device_create(cls: &user_class, NULL, devt: dev, NULL, fmt: "%s" , name); |
62 | else |
63 | device = device_create(cls: &class, NULL, devt: dev, NULL, fmt: "%s" , name); |
64 | |
65 | if (IS_ERR(ptr: device)) { |
66 | ret = PTR_ERR(ptr: device); |
67 | device = NULL; |
68 | pr_err("Could not create device for minor %d, %s (err %d)\n" , |
69 | minor, name, -ret); |
70 | cdev_del(cdev); |
71 | } |
72 | done: |
73 | *devp = device; |
74 | return ret; |
75 | } |
76 | |
77 | void hfi1_cdev_cleanup(struct cdev *cdev, struct device **devp) |
78 | { |
79 | struct device *device = *devp; |
80 | |
81 | if (device) { |
82 | device_unregister(dev: device); |
83 | *devp = NULL; |
84 | |
85 | cdev_del(cdev); |
86 | } |
87 | } |
88 | |
89 | static const char *hfi1_class_name = "hfi1" ; |
90 | |
91 | const char *class_name(void) |
92 | { |
93 | return hfi1_class_name; |
94 | } |
95 | |
96 | int __init dev_init(void) |
97 | { |
98 | int ret; |
99 | |
100 | ret = alloc_chrdev_region(&hfi1_dev, 0, HFI1_NMINORS, DRIVER_NAME); |
101 | if (ret < 0) { |
102 | pr_err("Could not allocate chrdev region (err %d)\n" , -ret); |
103 | goto done; |
104 | } |
105 | |
106 | ret = class_register(class: &class); |
107 | if (ret) { |
108 | pr_err("Could not create device class (err %d)\n" , -ret); |
109 | unregister_chrdev_region(hfi1_dev, HFI1_NMINORS); |
110 | goto done; |
111 | } |
112 | |
113 | ret = class_register(class: &user_class); |
114 | if (ret) { |
115 | pr_err("Could not create device class for user accessible files (err %d)\n" , |
116 | -ret); |
117 | class_unregister(class: &class); |
118 | unregister_chrdev_region(hfi1_dev, HFI1_NMINORS); |
119 | goto done; |
120 | } |
121 | |
122 | done: |
123 | return ret; |
124 | } |
125 | |
126 | void dev_cleanup(void) |
127 | { |
128 | class_unregister(class: &class); |
129 | class_unregister(class: &user_class); |
130 | |
131 | unregister_chrdev_region(hfi1_dev, HFI1_NMINORS); |
132 | } |
133 | |