xref: /freebsd/sys/dev/smbus/smb.c (revision 9336e0699bda8a301cd2bfa37106b6ec5e32012e)
1 /*-
2  * Copyright (c) 1998, 2001 Nicolas Souchu
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  * $FreeBSD$
27  */
28 
29 #ifdef HAVE_KERNEL_OPTION_HEADERS
30 #include "opt_compat.h"
31 #endif
32 
33 #include <sys/param.h>
34 #include <sys/kernel.h>
35 #include <sys/systm.h>
36 #include <sys/module.h>
37 #include <sys/bus.h>
38 #include <sys/conf.h>
39 #include <sys/uio.h>
40 #include <sys/fcntl.h>
41 
42 #include <dev/smbus/smbconf.h>
43 #include <dev/smbus/smbus.h>
44 #include <dev/smbus/smb.h>
45 
46 #include "smbus_if.h"
47 
48 #define BUFSIZE 1024
49 
50 struct smb_softc {
51 
52 	int sc_count;			/* >0 if device opened */
53 	struct cdev *sc_devnode;
54 };
55 
56 #define IIC_SOFTC(unit) \
57 	((struct smb_softc *)devclass_get_softc(smb_devclass, (unit)))
58 
59 #define IIC_DEVICE(unit) \
60 	(devclass_get_device(smb_devclass, (unit)))
61 
62 static void smb_identify(driver_t *driver, device_t parent);
63 static int smb_probe(device_t);
64 static int smb_attach(device_t);
65 static int smb_detach(device_t);
66 
67 static devclass_t smb_devclass;
68 
69 static device_method_t smb_methods[] = {
70 	/* device interface */
71 	DEVMETHOD(device_identify,	smb_identify),
72 	DEVMETHOD(device_probe,		smb_probe),
73 	DEVMETHOD(device_attach,	smb_attach),
74 	DEVMETHOD(device_detach,	smb_detach),
75 
76 	/* smbus interface */
77 	DEVMETHOD(smbus_intr,		smbus_generic_intr),
78 
79 	{ 0, 0 }
80 };
81 
82 static driver_t smb_driver = {
83 	"smb",
84 	smb_methods,
85 	sizeof(struct smb_softc),
86 };
87 
88 static	d_open_t	smbopen;
89 static	d_close_t	smbclose;
90 static	d_ioctl_t	smbioctl;
91 
92 static struct cdevsw smb_cdevsw = {
93 	.d_version =	D_VERSION,
94 	.d_flags =	D_NEEDGIANT,
95 	.d_open =	smbopen,
96 	.d_close =	smbclose,
97 	.d_ioctl =	smbioctl,
98 	.d_name =	"smb",
99 };
100 
101 static void
102 smb_identify(driver_t *driver, device_t parent)
103 {
104 
105 	if (device_find_child(parent, "smb", -1) == NULL)
106 		BUS_ADD_CHILD(parent, 0, "smb", -1);
107 }
108 
109 static int
110 smb_probe(device_t dev)
111 {
112 	device_set_desc(dev, "SMBus generic I/O");
113 
114 	return (0);
115 }
116 
117 static int
118 smb_attach(device_t dev)
119 {
120 	struct smb_softc *sc = (struct smb_softc *)device_get_softc(dev);
121 
122 	if (!sc)
123 		return (ENOMEM);
124 
125 	bzero(sc, sizeof(struct smb_softc *));
126 
127 	sc->sc_devnode = make_dev(&smb_cdevsw, device_get_unit(dev),
128 			UID_ROOT, GID_WHEEL,
129 			0600, "smb%d", device_get_unit(dev));
130 
131 	return (0);
132 }
133 
134 static int
135 smb_detach(device_t dev)
136 {
137 	struct smb_softc *sc = (struct smb_softc *)device_get_softc(dev);
138 
139 	if (sc->sc_devnode)
140 		destroy_dev(sc->sc_devnode);
141 
142 	return (0);
143 }
144 
145 static int
146 smbopen (struct cdev *dev, int flags, int fmt, struct thread *td)
147 {
148 	struct smb_softc *sc = IIC_SOFTC(minor(dev));
149 
150 	if (sc == NULL)
151 		return (ENXIO);
152 
153 	if (sc->sc_count != 0)
154 		return (EBUSY);
155 
156 	sc->sc_count++;
157 
158 	return (0);
159 }
160 
161 static int
162 smbclose(struct cdev *dev, int flags, int fmt, struct thread *td)
163 {
164 	struct smb_softc *sc = IIC_SOFTC(minor(dev));
165 
166 	if (sc == NULL)
167 		return (ENXIO);
168 
169 	if (sc->sc_count == 0)
170 		/* This is not supposed to happen. */
171 		return (0);
172 
173 	sc->sc_count--;
174 
175 	return (0);
176 }
177 
178 static int
179 smbioctl(struct cdev *dev, u_long cmd, caddr_t data, int flags, struct thread *td)
180 {
181 	char buf[SMB_MAXBLOCKSIZE];
182 	device_t parent;
183 	struct smbcmd *s = (struct smbcmd *)data;
184 	struct smb_softc *sc = IIC_SOFTC(minor(dev));
185 	device_t smbdev = IIC_DEVICE(minor(dev));
186 	int error;
187 	short w;
188 	u_char count;
189 	char c;
190 
191 	if (sc == NULL)
192 		return (ENXIO);
193 	if (s == NULL)
194 		return (EINVAL);
195 
196 	parent = device_get_parent(smbdev);
197 
198 	/* Allocate the bus. */
199 	if ((error = smbus_request_bus(parent, smbdev,
200 			(flags & O_NONBLOCK) ? SMB_DONTWAIT : (SMB_WAIT | SMB_INTR))))
201 		return (error);
202 
203 	switch (cmd) {
204 	case SMB_QUICK_WRITE:
205 		error = smbus_error(smbus_quick(parent, s->slave, SMB_QWRITE));
206 		break;
207 
208 	case SMB_QUICK_READ:
209 		error = smbus_error(smbus_quick(parent, s->slave, SMB_QREAD));
210 		break;
211 
212 	case SMB_SENDB:
213 		error = smbus_error(smbus_sendb(parent, s->slave, s->cmd));
214 		break;
215 
216 	case SMB_RECVB:
217 		error = smbus_error(smbus_recvb(parent, s->slave, &s->cmd));
218 		break;
219 
220 	case SMB_WRITEB:
221 		error = smbus_error(smbus_writeb(parent, s->slave, s->cmd,
222 						s->data.byte));
223 		break;
224 
225 	case SMB_WRITEW:
226 		error = smbus_error(smbus_writew(parent, s->slave,
227 						s->cmd, s->data.word));
228 		break;
229 
230 	case SMB_READB:
231 		if (s->data.byte_ptr) {
232 			error = smbus_error(smbus_readb(parent, s->slave,
233 						s->cmd, &c));
234 			if (error)
235 				break;
236 			error = copyout(&c, s->data.byte_ptr,
237 					sizeof(*(s->data.byte_ptr)));
238 		}
239 		break;
240 
241 	case SMB_READW:
242 		if (s->data.word_ptr) {
243 			error = smbus_error(smbus_readw(parent, s->slave,
244 						s->cmd, &w));
245 			if (error == 0) {
246 				error = copyout(&w, s->data.word_ptr,
247 						sizeof(*(s->data.word_ptr)));
248 			}
249 		}
250 		break;
251 
252 	case SMB_PCALL:
253 		if (s->data.process.rdata) {
254 
255 			error = smbus_error(smbus_pcall(parent, s->slave, s->cmd,
256 				s->data.process.sdata, &w));
257 			if (error)
258 				break;
259 			error = copyout(&w, s->data.process.rdata,
260 					sizeof(*(s->data.process.rdata)));
261 		}
262 
263 		break;
264 
265 	case SMB_BWRITE:
266 		if (s->count && s->data.byte_ptr) {
267 			if (s->count > SMB_MAXBLOCKSIZE)
268 				s->count = SMB_MAXBLOCKSIZE;
269 			error = copyin(s->data.byte_ptr, buf, s->count);
270 			if (error)
271 				break;
272 			error = smbus_error(smbus_bwrite(parent, s->slave,
273 						s->cmd, s->count, buf));
274 		}
275 		break;
276 
277 #if defined(COMPAT_FREEBSD4) || defined(COMPAT_FREEBSD5) || defined(COMPAT_FREEBSD6)
278 	case SMB_OLD_BREAD:
279 #endif
280 	case SMB_BREAD:
281 		if (s->count && s->data.byte_ptr) {
282 			count = min(s->count, SMB_MAXBLOCKSIZE);
283 			error = smbus_error(smbus_bread(parent, s->slave,
284 						s->cmd, &count, buf));
285 			if (error)
286 				break;
287 			error = copyout(buf, s->data.byte_ptr,
288 			    min(count, s->count));
289 			s->count = count;
290 		}
291 		break;
292 
293 	default:
294 		error = ENOTTY;
295 	}
296 
297 	smbus_release_bus(parent, smbdev);
298 
299 	return (error);
300 }
301 
302 DRIVER_MODULE(smb, smbus, smb_driver, smb_devclass, 0, 0);
303 MODULE_DEPEND(smb, smbus, SMBUS_MINVER, SMBUS_PREFVER, SMBUS_MAXVER);
304 MODULE_VERSION(smb, 1);
305