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