1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 */
25
26
27 #include <sys/types.h>
28 #include <sys/conf.h>
29 #include <sys/cmn_err.h>
30 #include <sys/kmem.h>
31 #include <sys/open.h>
32 #include <sys/stat.h>
33 #include <sys/modctl.h>
34 #include <sys/errno.h>
35 #include <sys/ddi.h>
36 #include <sys/sunddi.h>
37
38 /* #define SBUSMEM_DEBUG */
39
40 #ifdef SBUSMEM_DEBUG
41 #include <sys/ddi_impldefs.h>
42
43 int sbusmem_debug_flag;
44 #define sbusmem_debug if (sbusmem_debug_flag) printf
45 #endif /* SBUSMEM_DEBUG */
46
47 static void *sbusmem_state_head;
48
49 struct sbusmem_unit {
50 uint_t size;
51 uint_t pagesize;
52 dev_info_t *dip;
53 };
54
55 static int sbmem_open(dev_t *, int, int, cred_t *);
56 static int sbmem_close(dev_t, int, int, struct cred *);
57 static int sbmem_read(dev_t, struct uio *, cred_t *);
58 static int sbmem_write(dev_t, struct uio *, cred_t *);
59 static int sbmem_devmap(dev_t, devmap_cookie_t, offset_t, size_t,
60 size_t *, uint_t);
61
62 static struct cb_ops sbmem_cb_ops = {
63 sbmem_open, /* open */
64 sbmem_close, /* close */
65 nodev, /* strategy */
66 nodev, /* print */
67 nodev, /* dump */
68 sbmem_read, /* read */
69 sbmem_write, /* write */
70 nodev, /* ioctl */
71 sbmem_devmap, /* devmap */
72 nodev, /* mmap */
73 ddi_devmap_segmap, /* segmap */
74 nochpoll, /* poll */
75 ddi_prop_op, /* cb_prop_op */
76 0, /* streamtab */
77 D_NEW|D_MP|D_DEVMAP|D_HOTPLUG, /* Driver compatibility flag */
78 CB_REV, /* rev */
79 nodev, /* int (*cb_aread)() */
80 nodev /* int (*cb_awrite)() */
81 };
82
83 static int sbmem_attach(dev_info_t *, ddi_attach_cmd_t);
84 static int sbmem_detach(dev_info_t *, ddi_detach_cmd_t);
85 static int sbmem_info(dev_info_t *, ddi_info_cmd_t, void *, void **);
86
87 static struct dev_ops sbmem_ops = {
88 DEVO_REV, /* devo_rev, */
89 0, /* refcnt */
90 sbmem_info, /* get_dev_info */
91 nulldev, /* identify */
92 nulldev, /* probe */
93 sbmem_attach, /* attach */
94 sbmem_detach, /* detach */
95 nodev, /* reset */
96 &sbmem_cb_ops, /* driver operations */
97 (struct bus_ops *)0, /* bus operations */
98 nulldev, /* power */
99 ddi_quiesce_not_needed, /* quiesce */
100 };
101
102 static struct modldrv modldrv = {
103 &mod_driverops, /* Type of module. This one is a driver */
104 "SBus memory driver", /* Name of module. */
105 &sbmem_ops, /* driver ops */
106 };
107
108 static struct modlinkage modlinkage = {
109 MODREV_1, (void *)&modldrv, NULL
110 };
111
112 static int sbmem_rw(dev_t, struct uio *, enum uio_rw, cred_t *);
113
114 int
_init(void)115 _init(void)
116 {
117 int error;
118
119 if ((error = ddi_soft_state_init(&sbusmem_state_head,
120 sizeof (struct sbusmem_unit), 1)) != 0) {
121 return (error);
122 }
123 if ((error = mod_install(&modlinkage)) != 0) {
124 ddi_soft_state_fini(&sbusmem_state_head);
125 }
126 return (error);
127 }
128
129 int
_fini(void)130 _fini(void)
131 {
132 int error;
133
134 if ((error = mod_remove(&modlinkage)) == 0) {
135 ddi_soft_state_fini(&sbusmem_state_head);
136 }
137 return (error);
138 }
139
140 int
_info(struct modinfo * modinfop)141 _info(struct modinfo *modinfop)
142 {
143 return (mod_info(&modlinkage, modinfop));
144 }
145
146 static int
sbmem_attach(dev_info_t * devi,ddi_attach_cmd_t cmd)147 sbmem_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
148 {
149 struct sbusmem_unit *un;
150 int error = DDI_FAILURE;
151 int instance, ilen;
152 uint_t size;
153 char *ident;
154
155 switch (cmd) {
156 case DDI_ATTACH:
157 instance = ddi_get_instance(devi);
158
159 size = ddi_getprop(DDI_DEV_T_NONE, devi,
160 DDI_PROP_DONTPASS | DDI_PROP_CANSLEEP, "size", -1);
161 if (size == (uint_t)-1) {
162 #ifdef SBUSMEM_DEBUG
163 sbusmem_debug(
164 "sbmem_attach%d: No size property\n", instance);
165 #endif /* SBUSMEM_DEBUG */
166 break;
167 }
168
169 #ifdef SBUSMEM_DEBUG
170 {
171 struct regspec *rp = ddi_rnumber_to_regspec(devi, 0);
172
173 if (rp == NULL) {
174 sbusmem_debug(
175 "sbmem_attach%d: No reg property\n", instance);
176 } else {
177 sbusmem_debug(
178 "sbmem_attach%d: slot 0x%x size 0x%x\n", instance,
179 rp->regspec_bustype, rp->regspec_size);
180 }
181 }
182 #endif /* SBUSMEM_DEBUG */
183
184 if (ddi_getlongprop(DDI_DEV_T_ANY, devi,
185 DDI_PROP_DONTPASS | DDI_PROP_CANSLEEP, "ident",
186 (caddr_t)&ident, &ilen) != DDI_PROP_SUCCESS) {
187 #ifdef SBUSMEM_DEBUG
188 sbusmem_debug(
189 "sbmem_attach%d: No ident property\n", instance);
190 #endif /* SBUSMEM_DEBUG */
191 break;
192 }
193
194 if (ddi_soft_state_zalloc(sbusmem_state_head,
195 instance) != DDI_SUCCESS)
196 break;
197
198 if ((un = ddi_get_soft_state(sbusmem_state_head,
199 instance)) == NULL) {
200 ddi_soft_state_free(sbusmem_state_head, instance);
201 break;
202 }
203
204 if (ddi_create_minor_node(devi, ident, S_IFCHR, instance,
205 DDI_PSEUDO, 0) == DDI_FAILURE) {
206 kmem_free(ident, ilen);
207 ddi_remove_minor_node(devi, NULL);
208 ddi_soft_state_free(sbusmem_state_head, instance);
209 break;
210 }
211 kmem_free(ident, ilen);
212 un->dip = devi;
213 un->size = size;
214 un->pagesize = ddi_ptob(devi, 1);
215
216 #ifdef SBUSMEM_DEBUG
217 sbusmem_debug("sbmem_attach%d: dip 0x%p size 0x%x\n",
218 instance, devi, size);
219 #endif /* SBUSMEM_DEBUG */
220
221 ddi_report_dev(devi);
222 error = DDI_SUCCESS;
223 break;
224 case DDI_RESUME:
225 error = DDI_SUCCESS;
226 break;
227 default:
228 break;
229 }
230 return (error);
231 }
232
233 static int
sbmem_detach(dev_info_t * devi,ddi_detach_cmd_t cmd)234 sbmem_detach(dev_info_t *devi, ddi_detach_cmd_t cmd)
235 {
236 int instance;
237
238 switch (cmd) {
239 case DDI_DETACH:
240 instance = ddi_get_instance(devi);
241 ddi_remove_minor_node(devi, NULL);
242 ddi_soft_state_free(sbusmem_state_head, instance);
243 return (DDI_SUCCESS);
244
245 case DDI_SUSPEND:
246 return (DDI_SUCCESS);
247 }
248 return (DDI_FAILURE);
249 }
250
251 /*ARGSUSED1*/
252 static int
sbmem_open(dev_t * devp,int flag,int typ,cred_t * cred)253 sbmem_open(dev_t *devp, int flag, int typ, cred_t *cred)
254 {
255 int instance;
256
257 if (typ != OTYP_CHR)
258 return (EINVAL);
259
260 instance = getminor(*devp);
261 if (ddi_get_soft_state(sbusmem_state_head, instance) == NULL) {
262 return (ENXIO);
263 }
264 return (0);
265 }
266
267 /*ARGSUSED*/
268 static int
sbmem_close(dev_t dev,int flag,int otyp,struct cred * cred)269 sbmem_close(dev_t dev, int flag, int otyp, struct cred *cred)
270 {
271 if (otyp != OTYP_CHR)
272 return (EINVAL);
273
274 return (0);
275 }
276
277 static int
sbmem_info(dev_info_t * dip __unused,ddi_info_cmd_t infocmd,void * arg,void ** result)278 sbmem_info(dev_info_t *dip __unused, ddi_info_cmd_t infocmd, void *arg,
279 void **result)
280 {
281 int instance, error = DDI_FAILURE;
282 struct sbusmem_unit *un;
283
284 switch (infocmd) {
285 case DDI_INFO_DEVT2DEVINFO:
286 instance = getminor((dev_t)arg);
287 if ((un = ddi_get_soft_state(sbusmem_state_head,
288 instance)) != NULL) {
289 *result = (void *)un->dip;
290 error = DDI_SUCCESS;
291 #ifdef SBUSMEM_DEBUG
292 sbusmem_debug(
293 "sbmem_info%d: returning dip 0x%p\n", instance, un->dip);
294 #endif /* SBUSMEM_DEBUG */
295
296 }
297 break;
298
299 case DDI_INFO_DEVT2INSTANCE:
300 instance = getminor((dev_t)arg);
301 *result = (void *)(uintptr_t)instance;
302 error = DDI_SUCCESS;
303 break;
304
305 default:
306 break;
307 }
308 return (error);
309 }
310
311 static int
sbmem_read(dev_t dev,struct uio * uio,cred_t * cred)312 sbmem_read(dev_t dev, struct uio *uio, cred_t *cred)
313 {
314 return (sbmem_rw(dev, uio, UIO_READ, cred));
315 }
316
317 static int
sbmem_write(dev_t dev,struct uio * uio,cred_t * cred)318 sbmem_write(dev_t dev, struct uio *uio, cred_t *cred)
319 {
320 return (sbmem_rw(dev, uio, UIO_WRITE, cred));
321 }
322
323 static int
sbmem_rw(dev_t dev,struct uio * uio,enum uio_rw rw,cred_t * cred __unused)324 sbmem_rw(dev_t dev, struct uio *uio, enum uio_rw rw, cred_t *cred __unused)
325 {
326 uint_t c;
327 struct iovec *iov;
328 struct sbusmem_unit *un;
329 uint_t pagesize, msize;
330 int instance, error = 0;
331 dev_info_t *dip;
332 caddr_t reg;
333
334 instance = getminor(dev);
335 if ((un = ddi_get_soft_state(sbusmem_state_head, instance)) == NULL) {
336 return (ENXIO);
337 }
338 dip = un->dip;
339 pagesize = un->pagesize;
340
341 while (uio->uio_resid > 0 && error == 0) {
342 iov = uio->uio_iov;
343 if (iov->iov_len == 0) {
344 uio->uio_iov++;
345 uio->uio_iovcnt--;
346 if (uio->uio_iovcnt < 0)
347 cmn_err(CE_PANIC, "sbmem_rw");
348 continue;
349 }
350
351 if (uio->uio_offset > un->size) {
352 return (EFAULT);
353 }
354
355 if (uio->uio_offset == un->size) {
356 return (0); /* EOF */
357 }
358 msize = pagesize - (uio->uio_offset & (pagesize - 1));
359 if (ddi_map_regs(dip, 0, ®, uio->uio_offset,
360 (off_t)msize) != DDI_SUCCESS) {
361 return (EFAULT);
362 }
363 c = min(msize, (uint_t)iov->iov_len);
364 if (ddi_peekpokeio(dip, uio, rw, reg, (int)c,
365 sizeof (int)) != DDI_SUCCESS)
366 error = EFAULT;
367
368 ddi_unmap_regs(dip, 0, ®, uio->uio_offset, (off_t)msize);
369 }
370 return (error);
371 }
372
373 static int
sbmem_devmap(dev_t dev,devmap_cookie_t dhp,offset_t off,size_t len,size_t * maplen,uint_t model __unused)374 sbmem_devmap(dev_t dev, devmap_cookie_t dhp, offset_t off, size_t len,
375 size_t *maplen, uint_t model __unused)
376 {
377 struct sbusmem_unit *un;
378 int instance, error;
379
380 instance = getminor(dev);
381 if ((un = ddi_get_soft_state(sbusmem_state_head, instance)) == NULL) {
382 return (ENXIO);
383 }
384 if (off + len > un->size) {
385 return (ENXIO);
386 }
387 if ((error = devmap_devmem_setup(dhp, un->dip, NULL, 0,
388 off, len, PROT_ALL, DEVMAP_DEFAULTS, NULL)) < 0) {
389 return (error);
390 }
391 *maplen = ptob(btopr(len));
392 return (0);
393 }
394