]> git.proxmox.com Git - mirror_spl.git/blob - modules/spl/spl-module.c
cd3bc88ef6baa8b41840a0dfdd56bc31de90d5f8
[mirror_spl.git] / modules / spl / spl-module.c
1 #include <sys/sysmacros.h>
2 #include <sys/sunddi.h>
3 #include "config.h"
4
5 #ifdef DEBUG_SUBSYSTEM
6 #undef DEBUG_SUBSYSTEM
7 #endif
8
9 #define DEBUG_SUBSYSTEM S_MODULE
10
11 static spinlock_t dev_info_lock = SPIN_LOCK_UNLOCKED;
12 static LIST_HEAD(dev_info_list);
13
14 static struct dev_info *
15 get_dev_info(dev_t dev)
16 {
17 struct dev_info *di;
18
19 spin_lock(&dev_info_lock);
20
21 list_for_each_entry(di, &dev_info_list, di_list)
22 if (di->di_dev == dev)
23 goto out;
24
25 di = NULL;
26 out:
27 spin_unlock(&dev_info_lock);
28 return di;
29 }
30
31 static int
32 mod_generic_ioctl(struct inode *ino, struct file *filp,
33 unsigned int cmd, unsigned long arg)
34 {
35 struct dev_info *di;
36 int rc, flag = 0, rvalp = 0;
37 cred_t *cr = NULL;
38
39 di = get_dev_info(MKDEV(imajor(ino), iminor(ino)));
40 if (di == NULL)
41 return EINVAL;
42
43 rc = di->di_ops->devo_cb_ops->cb_ioctl(di->di_dev,
44 (int)cmd,(intptr_t)arg,
45 flag, cr, &rvalp);
46 return rc;
47 }
48
49 int
50 __ddi_create_minor_node(dev_info_t *di, char *name, int spec_type,
51 minor_t minor_num, char *node_type,
52 int flag, struct module *mod)
53 {
54 struct cdev *cdev;
55 struct dev_ops *dev_ops;
56 struct cb_ops *cb_ops;
57 struct file_operations *fops;
58 int rc;
59 ENTRY;
60
61 ASSERT(spec_type == S_IFCHR);
62 ASSERT(minor_num < di->di_minors);
63 ASSERT(!strcmp(node_type, DDI_PSEUDO));
64 ASSERT(flag == 0);
65
66 fops = kzalloc(sizeof(struct file_operations), GFP_KERNEL);
67 if (fops == NULL)
68 RETURN(DDI_FAILURE);
69
70 cdev = cdev_alloc();
71 if (cdev == NULL) {
72 kfree(fops);
73 RETURN(DDI_FAILURE);
74 }
75
76 cdev->ops = fops;
77
78 mutex_enter(&di->di_lock);
79 dev_ops = di->di_ops;
80 ASSERT(dev_ops);
81 cb_ops = di->di_ops->devo_cb_ops;
82 ASSERT(cb_ops);
83
84 /* Setup the fops to cb_ops mapping */
85 fops->owner = mod;
86 if (cb_ops->cb_ioctl)
87 fops->ioctl = mod_generic_ioctl;
88
89 #if 0
90 if (cb_ops->cb_open)
91 fops->open = mod_generic_open;
92
93 if (cb_ops->cb_close)
94 fops->release = mod_generic_close;
95
96 if (cb_ops->cb_read)
97 fops->read = mod_generic_read;
98
99 if (cb_ops->cb_write)
100 fops->write = mod_generic_write;
101 #endif
102 /* XXX: Currently unsupported operations */
103 ASSERT(cb_ops->cb_open == NULL);
104 ASSERT(cb_ops->cb_close == NULL);
105 ASSERT(cb_ops->cb_read == NULL);
106 ASSERT(cb_ops->cb_write == NULL);
107 ASSERT(cb_ops->cb_strategy == NULL);
108 ASSERT(cb_ops->cb_print == NULL);
109 ASSERT(cb_ops->cb_dump == NULL);
110 ASSERT(cb_ops->cb_devmap == NULL);
111 ASSERT(cb_ops->cb_mmap == NULL);
112 ASSERT(cb_ops->cb_segmap == NULL);
113 ASSERT(cb_ops->cb_chpoll == NULL);
114 ASSERT(cb_ops->cb_prop_op == NULL);
115 ASSERT(cb_ops->cb_str == NULL);
116 ASSERT(cb_ops->cb_aread == NULL);
117 ASSERT(cb_ops->cb_awrite == NULL);
118
119 di->di_minor = minor_num;
120 di->di_dev = MKDEV(di->di_major, di->di_minor);
121
122 rc = cdev_add(cdev, di->di_dev, 1);
123 if (rc) {
124 CERROR("Error adding cdev, %d\n", rc);
125 kfree(fops);
126 cdev_del(cdev);
127 mutex_exit(&di->di_lock);
128 RETURN(DDI_FAILURE);
129 }
130
131 di->di_class = class_create(THIS_MODULE, name);
132 if (IS_ERR(di->di_class)) {
133 rc = PTR_ERR(di->di_class);
134 CERROR("Error creating %s class, %d\n", name, rc);
135 kfree(fops);
136 cdev_del(di->di_cdev);
137 mutex_exit(&di->di_lock);
138 RETURN(DDI_FAILURE);
139 }
140
141 /* Do not append a 0 to devices with minor nums of 0 */
142 if (di->di_minor == 0) {
143 class_device_create(di->di_class, NULL, di->di_dev,
144 NULL, "%s", name);
145 } else {
146 class_device_create(di->di_class, NULL, di->di_dev,
147 NULL, "%s%d", name, di->di_minor);
148 }
149
150 di->di_cdev = cdev;
151
152 spin_lock(&dev_info_lock);
153 list_add(&di->di_list, &dev_info_list);
154 spin_unlock(&dev_info_lock);
155
156 mutex_exit(&di->di_lock);
157
158 RETURN(DDI_SUCCESS);
159 }
160 EXPORT_SYMBOL(__ddi_create_minor_node);
161
162 static void
163 __ddi_remove_minor_node_locked(dev_info_t *di, char *name)
164 {
165 if (di->di_class) {
166 class_device_destroy(di->di_class, di->di_dev);
167 class_destroy(di->di_class);
168
169 di->di_class = NULL;
170 di->di_dev = 0;
171 }
172
173 if (di->di_cdev) {
174 cdev_del(di->di_cdev);
175 di->di_cdev = NULL;
176 }
177
178 spin_lock(&dev_info_lock);
179 list_del_init(&di->di_list);
180 spin_unlock(&dev_info_lock);
181 }
182
183 void
184 __ddi_remove_minor_node(dev_info_t *di, char *name)
185 {
186 ENTRY;
187 mutex_enter(&di->di_lock);
188 __ddi_remove_minor_node_locked(di, name);
189 mutex_exit(&di->di_lock);
190 EXIT;
191 }
192 EXPORT_SYMBOL(ddi_remove_minor_node);
193
194 #if 0
195 static int
196 mod_generic_open(struct inode *, struct file *)
197 {
198 open(dev_t *devp, int flag, int otyp, cred_t *credp);
199 }
200
201 static int
202 mod_generic_close(struct inode *, struct file *)
203 {
204 close(dev_t dev, int flag, int otyp, cred_t *credp);
205 }
206
207 static ssize_t
208 mod_generic_read(struct file *, char __user *, size_t, loff_t *)
209 {
210 read(dev_t dev, struct uio *uiop, cred_t *credp);
211 }
212
213 static ssize_t
214 mod_generic_write(struct file *, const char __user *, size_t, loff_t *)
215 {
216 write(dev_t dev, struct uio *uiop, cred_t *credp);
217 }
218 #endif
219
220 static struct dev_info *
221 dev_info_alloc(major_t major, minor_t minors, struct dev_ops *ops) {
222 struct dev_info *di;
223
224 di = kmalloc(sizeof(struct dev_info), GFP_KERNEL);
225 if (di == NULL)
226 return NULL;
227
228 mutex_init(&di->di_lock, NULL, MUTEX_DEFAULT, NULL);
229 INIT_LIST_HEAD(&di->di_list);
230 di->di_ops = ops;
231 di->di_class = NULL;
232 di->di_cdev = NULL;
233 di->di_major = major;
234 di->di_minor = 0;
235 di->di_minors = minors;
236 di->di_dev = 0;
237
238 return di;
239 }
240
241 static void
242 dev_info_free(struct dev_info *di)
243 {
244 mutex_enter(&di->di_lock);
245 __ddi_remove_minor_node_locked(di, NULL);
246 mutex_exit(&di->di_lock);
247 mutex_destroy(&di->di_lock);
248 kfree(di);
249 }
250
251 int
252 __mod_install(struct modlinkage *modlp)
253 {
254 struct modldrv *drv = modlp->ml_modldrv;
255 struct dev_info *di;
256 int rc;
257 ENTRY;
258
259 di = dev_info_alloc(modlp->ml_major, modlp->ml_minors,
260 drv->drv_dev_ops);
261 if (di == NULL)
262 RETURN(ENOMEM);
263
264 /* XXX: Really we need to be calling devo_probe if it's available
265 * and then calling devo_attach for each device discovered. However
266 * for now we just call it once and let the app sort it out.
267 */
268 rc = drv->drv_dev_ops->devo_attach(di, DDI_ATTACH);
269 if (rc != DDI_SUCCESS) {
270 dev_info_free(di);
271 RETURN(rc);
272 }
273
274 drv->drv_dev_info = di;
275
276 RETURN(DDI_SUCCESS);
277 }
278 EXPORT_SYMBOL(__mod_install);
279
280 int
281 __mod_remove(struct modlinkage *modlp)
282 {
283 struct modldrv *drv = modlp->ml_modldrv;
284 struct dev_info *di = drv->drv_dev_info;
285 int rc;
286 ENTRY;
287
288 rc = drv->drv_dev_ops->devo_detach(di, DDI_DETACH);
289 if (rc != DDI_SUCCESS)
290 RETURN(rc);
291
292 dev_info_free(di);
293 drv->drv_dev_info = NULL;
294
295 RETURN(DDI_SUCCESS);
296 }
297 EXPORT_SYMBOL(__mod_remove);
298
299 int
300 ldi_ident_from_mod(struct modlinkage *modlp, ldi_ident_t *lip)
301 {
302 ldi_ident_t li;
303 ENTRY;
304
305 ASSERT(modlp);
306 ASSERT(lip);
307
308 li = kmalloc(sizeof(struct ldi_ident), GFP_KERNEL);
309 if (li == NULL)
310 RETURN(ENOMEM);
311
312 li->li_dev = MKDEV(modlp->ml_major, 0);
313 *lip = li;
314
315 RETURN(0);
316 }
317 EXPORT_SYMBOL(ldi_ident_from_mod);
318
319 void
320 ldi_ident_release(ldi_ident_t lip)
321 {
322 ENTRY;
323 ASSERT(lip);
324 kfree(lip);
325 EXIT;
326 }
327 EXPORT_SYMBOL(ldi_ident_release);