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 #if !defined(lint)
115 static char sbusmem_initmsg[] = "sbusmem _init: sbusmem.c\t1.28\t08/19/2008\n";
116 #endif
117
118 int
_init(void)119 _init(void)
120 {
121 int error;
122
123 if ((error = ddi_soft_state_init(&sbusmem_state_head,
124 sizeof (struct sbusmem_unit), 1)) != 0) {
125 return (error);
126 }
127 if ((error = mod_install(&modlinkage)) != 0) {
128 ddi_soft_state_fini(&sbusmem_state_head);
129 }
130 return (error);
131 }
132
133 int
_fini(void)134 _fini(void)
135 {
136 int error;
137
138 if ((error = mod_remove(&modlinkage)) == 0) {
139 ddi_soft_state_fini(&sbusmem_state_head);
140 }
141 return (error);
142 }
143
144 int
_info(struct modinfo * modinfop)145 _info(struct modinfo *modinfop)
146 {
147 return (mod_info(&modlinkage, modinfop));
148 }
149
150 static int
sbmem_attach(dev_info_t * devi,ddi_attach_cmd_t cmd)151 sbmem_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
152 {
153 struct sbusmem_unit *un;
154 int error = DDI_FAILURE;
155 int instance, ilen;
156 uint_t size;
157 char *ident;
158
159 switch (cmd) {
160 case DDI_ATTACH:
161 instance = ddi_get_instance(devi);
162
163 size = ddi_getprop(DDI_DEV_T_NONE, devi,
164 DDI_PROP_DONTPASS | DDI_PROP_CANSLEEP, "size", -1);
165 if (size == (uint_t)-1) {
166 #ifdef SBUSMEM_DEBUG
167 sbusmem_debug(
168 "sbmem_attach%d: No size property\n", instance);
169 #endif /* SBUSMEM_DEBUG */
170 break;
171 }
172
173 #ifdef SBUSMEM_DEBUG
174 {
175 struct regspec *rp = ddi_rnumber_to_regspec(devi, 0);
176
177 if (rp == NULL) {
178 sbusmem_debug(
179 "sbmem_attach%d: No reg property\n", instance);
180 } else {
181 sbusmem_debug(
182 "sbmem_attach%d: slot 0x%x size 0x%x\n", instance,
183 rp->regspec_bustype, rp->regspec_size);
184 }
185 }
186 #endif /* SBUSMEM_DEBUG */
187
188 if (ddi_getlongprop(DDI_DEV_T_ANY, devi,
189 DDI_PROP_DONTPASS | DDI_PROP_CANSLEEP, "ident",
190 (caddr_t)&ident, &ilen) != DDI_PROP_SUCCESS) {
191 #ifdef SBUSMEM_DEBUG
192 sbusmem_debug(
193 "sbmem_attach%d: No ident property\n", instance);
194 #endif /* SBUSMEM_DEBUG */
195 break;
196 }
197
198 if (ddi_soft_state_zalloc(sbusmem_state_head,
199 instance) != DDI_SUCCESS)
200 break;
201
202 if ((un = ddi_get_soft_state(sbusmem_state_head,
203 instance)) == NULL) {
204 ddi_soft_state_free(sbusmem_state_head, instance);
205 break;
206 }
207
208 if (ddi_create_minor_node(devi, ident, S_IFCHR, instance,
209 DDI_PSEUDO, NULL) == DDI_FAILURE) {
210 kmem_free(ident, ilen);
211 ddi_remove_minor_node(devi, NULL);
212 ddi_soft_state_free(sbusmem_state_head, instance);
213 break;
214 }
215 kmem_free(ident, ilen);
216 un->dip = devi;
217 un->size = size;
218 un->pagesize = ddi_ptob(devi, 1);
219
220 #ifdef SBUSMEM_DEBUG
221 sbusmem_debug("sbmem_attach%d: dip 0x%p size 0x%x\n",
222 instance, devi, size);
223 #endif /* SBUSMEM_DEBUG */
224
225 ddi_report_dev(devi);
226 error = DDI_SUCCESS;
227 break;
228 case DDI_RESUME:
229 error = DDI_SUCCESS;
230 break;
231 default:
232 break;
233 }
234 return (error);
235 }
236
237 static int
sbmem_detach(dev_info_t * devi,ddi_detach_cmd_t cmd)238 sbmem_detach(dev_info_t *devi, ddi_detach_cmd_t cmd)
239 {
240 int instance;
241
242 switch (cmd) {
243 case DDI_DETACH:
244 instance = ddi_get_instance(devi);
245 ddi_remove_minor_node(devi, NULL);
246 ddi_soft_state_free(sbusmem_state_head, instance);
247 return (DDI_SUCCESS);
248
249 case DDI_SUSPEND:
250 return (DDI_SUCCESS);
251 }
252 return (DDI_FAILURE);
253 }
254
255 /*ARGSUSED1*/
256 static int
sbmem_open(dev_t * devp,int flag,int typ,cred_t * cred)257 sbmem_open(dev_t *devp, int flag, int typ, cred_t *cred)
258 {
259 int instance;
260
261 if (typ != OTYP_CHR)
262 return (EINVAL);
263
264 instance = getminor(*devp);
265 if (ddi_get_soft_state(sbusmem_state_head, instance) == NULL) {
266 return (ENXIO);
267 }
268 return (0);
269 }
270
271 /*ARGSUSED*/
272 static int
sbmem_close(dev_t dev,int flag,int otyp,struct cred * cred)273 sbmem_close(dev_t dev, int flag, int otyp, struct cred *cred)
274 {
275 if (otyp != OTYP_CHR)
276 return (EINVAL);
277
278 return (0);
279 }
280
281 static int
sbmem_info(dev_info_t * dip,ddi_info_cmd_t infocmd,void * arg,void ** result)282 sbmem_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
283 {
284 int instance, error = DDI_FAILURE;
285 struct sbusmem_unit *un;
286
287 #if defined(lint) || defined(__lint)
288 dip = dip;
289 #endif /* lint || __lint */
290
291 switch (infocmd) {
292 case DDI_INFO_DEVT2DEVINFO:
293 instance = getminor((dev_t)arg);
294 if ((un = ddi_get_soft_state(sbusmem_state_head,
295 instance)) != NULL) {
296 *result = (void *)un->dip;
297 error = DDI_SUCCESS;
298 #ifdef SBUSMEM_DEBUG
299 sbusmem_debug(
300 "sbmem_info%d: returning dip 0x%p\n", instance, un->dip);
301 #endif /* SBUSMEM_DEBUG */
302
303 }
304 break;
305
306 case DDI_INFO_DEVT2INSTANCE:
307 instance = getminor((dev_t)arg);
308 *result = (void *)(uintptr_t)instance;
309 error = DDI_SUCCESS;
310 break;
311
312 default:
313 break;
314 }
315 return (error);
316 }
317
318 static int
sbmem_read(dev_t dev,struct uio * uio,cred_t * cred)319 sbmem_read(dev_t dev, struct uio *uio, cred_t *cred)
320 {
321 return (sbmem_rw(dev, uio, UIO_READ, cred));
322 }
323
324 static int
sbmem_write(dev_t dev,struct uio * uio,cred_t * cred)325 sbmem_write(dev_t dev, struct uio *uio, cred_t *cred)
326 {
327 return (sbmem_rw(dev, uio, UIO_WRITE, cred));
328 }
329
330 static int
sbmem_rw(dev_t dev,struct uio * uio,enum uio_rw rw,cred_t * cred)331 sbmem_rw(dev_t dev, struct uio *uio, enum uio_rw rw, cred_t *cred)
332 {
333 uint_t c;
334 struct iovec *iov;
335 struct sbusmem_unit *un;
336 uint_t pagesize, msize;
337 int instance, error = 0;
338 dev_info_t *dip;
339 caddr_t reg;
340
341 #if defined(lint) || defined(__lint)
342 cred = cred;
343 #endif /* lint || __lint */
344
345 instance = getminor(dev);
346 if ((un = ddi_get_soft_state(sbusmem_state_head, instance)) == NULL) {
347 return (ENXIO);
348 }
349 dip = un->dip;
350 pagesize = un->pagesize;
351
352 while (uio->uio_resid > 0 && error == 0) {
353 iov = uio->uio_iov;
354 if (iov->iov_len == 0) {
355 uio->uio_iov++;
356 uio->uio_iovcnt--;
357 if (uio->uio_iovcnt < 0)
358 cmn_err(CE_PANIC, "sbmem_rw");
359 continue;
360 }
361
362 if (uio->uio_offset > un->size) {
363 return (EFAULT);
364 }
365
366 if (uio->uio_offset == un->size) {
367 return (0); /* EOF */
368 }
369 msize = pagesize - (uio->uio_offset & (pagesize - 1));
370 if (ddi_map_regs(dip, 0, ®, uio->uio_offset,
371 (off_t)msize) != DDI_SUCCESS) {
372 return (EFAULT);
373 }
374 c = min(msize, (uint_t)iov->iov_len);
375 if (ddi_peekpokeio(dip, uio, rw, reg, (int)c,
376 sizeof (int)) != DDI_SUCCESS)
377 error = EFAULT;
378
379 ddi_unmap_regs(dip, 0, ®, uio->uio_offset, (off_t)msize);
380 }
381 return (error);
382 }
383
384 static int
sbmem_devmap(dev_t dev,devmap_cookie_t dhp,offset_t off,size_t len,size_t * maplen,uint_t model)385 sbmem_devmap(dev_t dev, devmap_cookie_t dhp, offset_t off, size_t len,
386 size_t *maplen, uint_t model)
387 {
388 struct sbusmem_unit *un;
389 int instance, error;
390
391 #if defined(lint) || defined(__lint)
392 model = model;
393 #endif /* lint || __lint */
394
395 instance = getminor(dev);
396 if ((un = ddi_get_soft_state(sbusmem_state_head, instance)) == NULL) {
397 return (ENXIO);
398 }
399 if (off + len > un->size) {
400 return (ENXIO);
401 }
402 if ((error = devmap_devmem_setup(dhp, un->dip, NULL, 0,
403 off, len, PROT_ALL, DEVMAP_DEFAULTS, NULL)) < 0) {
404 return (error);
405 }
406 *maplen = ptob(btopr(len));
407 return (0);
408 }
409