sbusmem.c revision 7656:2621e50fdf4a
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
43int sbusmem_debug_flag;
44#define	sbusmem_debug	if (sbusmem_debug_flag) printf
45#endif /* SBUSMEM_DEBUG */
46
47static void *sbusmem_state_head;
48
49struct sbusmem_unit {
50	uint_t size;
51	uint_t pagesize;
52	dev_info_t *dip;
53};
54
55static int sbmem_open(dev_t *, int, int, cred_t *);
56static int sbmem_close(dev_t, int, int, struct cred *);
57static int sbmem_read(dev_t, struct uio *, cred_t *);
58static int sbmem_write(dev_t, struct uio *, cred_t *);
59static int sbmem_devmap(dev_t, devmap_cookie_t, offset_t, size_t,
60		size_t *, uint_t);
61
62static 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
83static int sbmem_attach(dev_info_t *, ddi_attach_cmd_t);
84static int sbmem_detach(dev_info_t *, ddi_detach_cmd_t);
85static int sbmem_info(dev_info_t *, ddi_info_cmd_t, void *, void **);
86
87static 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
102static 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
108static struct modlinkage modlinkage = {
109	MODREV_1, (void *)&modldrv, NULL
110};
111
112static int sbmem_rw(dev_t, struct uio *, enum uio_rw, cred_t *);
113
114#if !defined(lint)
115static char sbusmem_initmsg[] = "sbusmem _init: sbusmem.c\t1.28\t08/19/2008\n";
116#endif
117
118int
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
133int
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
144int
145_info(struct modinfo *modinfop)
146{
147	return (mod_info(&modlinkage, modinfop));
148}
149
150static int
151sbmem_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
237static int
238sbmem_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*/
256static int
257sbmem_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*/
272static int
273sbmem_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
281static int
282sbmem_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
318static int
319sbmem_read(dev_t dev, struct uio *uio, cred_t *cred)
320{
321	return (sbmem_rw(dev, uio, UIO_READ, cred));
322}
323
324static int
325sbmem_write(dev_t dev, struct uio *uio, cred_t *cred)
326{
327	return (sbmem_rw(dev, uio, UIO_WRITE, cred));
328}
329
330static int
331sbmem_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, &reg, 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, &reg, uio->uio_offset, (off_t)msize);
380	}
381	return (error);
382}
383
384static int
385sbmem_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