xref: /freebsd/sys/dev/smbus/smb.c (revision 39beb93c)
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 	device_t sc_dev;
52 	int sc_count;			/* >0 if device opened */
53 	struct cdev *sc_devnode;
54 	struct mtx sc_lock;
55 };
56 
57 static void smb_identify(driver_t *driver, device_t parent);
58 static int smb_probe(device_t);
59 static int smb_attach(device_t);
60 static int smb_detach(device_t);
61 
62 static devclass_t smb_devclass;
63 
64 static device_method_t smb_methods[] = {
65 	/* device interface */
66 	DEVMETHOD(device_identify,	smb_identify),
67 	DEVMETHOD(device_probe,		smb_probe),
68 	DEVMETHOD(device_attach,	smb_attach),
69 	DEVMETHOD(device_detach,	smb_detach),
70 
71 	/* smbus interface */
72 	DEVMETHOD(smbus_intr,		smbus_generic_intr),
73 
74 	{ 0, 0 }
75 };
76 
77 static driver_t smb_driver = {
78 	"smb",
79 	smb_methods,
80 	sizeof(struct smb_softc),
81 };
82 
83 static	d_open_t	smbopen;
84 static	d_close_t	smbclose;
85 static	d_ioctl_t	smbioctl;
86 
87 static struct cdevsw smb_cdevsw = {
88 	.d_version =	D_VERSION,
89 	.d_flags =	D_TRACKCLOSE,
90 	.d_open =	smbopen,
91 	.d_close =	smbclose,
92 	.d_ioctl =	smbioctl,
93 	.d_name =	"smb",
94 };
95 
96 static void
97 smb_identify(driver_t *driver, device_t parent)
98 {
99 
100 	if (device_find_child(parent, "smb", -1) == NULL)
101 		BUS_ADD_CHILD(parent, 0, "smb", -1);
102 }
103 
104 static int
105 smb_probe(device_t dev)
106 {
107 	device_set_desc(dev, "SMBus generic I/O");
108 
109 	return (0);
110 }
111 
112 static int
113 smb_attach(device_t dev)
114 {
115 	struct smb_softc *sc = device_get_softc(dev);
116 
117 	sc->sc_dev = dev;
118 	sc->sc_devnode = make_dev(&smb_cdevsw, device_get_unit(dev),
119 	    UID_ROOT, GID_WHEEL, 0600, "smb%d", device_get_unit(dev));
120 	sc->sc_devnode->si_drv1 = sc;
121 	mtx_init(&sc->sc_lock, device_get_nameunit(dev), NULL, MTX_DEF);
122 
123 	return (0);
124 }
125 
126 static int
127 smb_detach(device_t dev)
128 {
129 	struct smb_softc *sc = (struct smb_softc *)device_get_softc(dev);
130 
131 	if (sc->sc_devnode)
132 		destroy_dev(sc->sc_devnode);
133 	mtx_destroy(&sc->sc_lock);
134 
135 	return (0);
136 }
137 
138 static int
139 smbopen(struct cdev *dev, int flags, int fmt, struct thread *td)
140 {
141 	struct smb_softc *sc = dev->si_drv1;
142 
143 	mtx_lock(&sc->sc_lock);
144 	if (sc->sc_count != 0) {
145 		mtx_unlock(&sc->sc_lock);
146 		return (EBUSY);
147 	}
148 
149 	sc->sc_count++;
150 	mtx_unlock(&sc->sc_lock);
151 
152 	return (0);
153 }
154 
155 static int
156 smbclose(struct cdev *dev, int flags, int fmt, struct thread *td)
157 {
158 	struct smb_softc *sc = dev->si_drv1;
159 
160 	mtx_lock(&sc->sc_lock);
161 	KASSERT(sc->sc_count == 1, ("device not busy"));
162 	sc->sc_count--;
163 	mtx_unlock(&sc->sc_lock);
164 
165 	return (0);
166 }
167 
168 static int
169 smbioctl(struct cdev *dev, u_long cmd, caddr_t data, int flags, struct thread *td)
170 {
171 	char buf[SMB_MAXBLOCKSIZE];
172 	device_t parent;
173 	struct smbcmd *s = (struct smbcmd *)data;
174 	struct smb_softc *sc = dev->si_drv1;
175 	device_t smbdev = sc->sc_dev;
176 	int error;
177 	short w;
178 	u_char count;
179 	char c;
180 
181 	parent = device_get_parent(smbdev);
182 
183 	/* Make sure that LSB bit is cleared. */
184 	if (s->slave & 0x1)
185 		return (EINVAL);
186 
187 	/* Allocate the bus. */
188 	if ((error = smbus_request_bus(parent, smbdev,
189 			(flags & O_NONBLOCK) ? SMB_DONTWAIT : (SMB_WAIT | SMB_INTR))))
190 		return (error);
191 
192 	switch (cmd) {
193 	case SMB_QUICK_WRITE:
194 		error = smbus_error(smbus_quick(parent, s->slave, SMB_QWRITE));
195 		break;
196 
197 	case SMB_QUICK_READ:
198 		error = smbus_error(smbus_quick(parent, s->slave, SMB_QREAD));
199 		break;
200 
201 	case SMB_SENDB:
202 		error = smbus_error(smbus_sendb(parent, s->slave, s->cmd));
203 		break;
204 
205 	case SMB_RECVB:
206 		error = smbus_error(smbus_recvb(parent, s->slave, &s->cmd));
207 		break;
208 
209 	case SMB_WRITEB:
210 		error = smbus_error(smbus_writeb(parent, s->slave, s->cmd,
211 						s->data.byte));
212 		break;
213 
214 	case SMB_WRITEW:
215 		error = smbus_error(smbus_writew(parent, s->slave,
216 						s->cmd, s->data.word));
217 		break;
218 
219 	case SMB_READB:
220 		if (s->data.byte_ptr) {
221 			error = smbus_error(smbus_readb(parent, s->slave,
222 						s->cmd, &c));
223 			if (error)
224 				break;
225 			error = copyout(&c, s->data.byte_ptr,
226 					sizeof(*(s->data.byte_ptr)));
227 		}
228 		break;
229 
230 	case SMB_READW:
231 		if (s->data.word_ptr) {
232 			error = smbus_error(smbus_readw(parent, s->slave,
233 						s->cmd, &w));
234 			if (error == 0) {
235 				error = copyout(&w, s->data.word_ptr,
236 						sizeof(*(s->data.word_ptr)));
237 			}
238 		}
239 		break;
240 
241 	case SMB_PCALL:
242 		if (s->data.process.rdata) {
243 
244 			error = smbus_error(smbus_pcall(parent, s->slave, s->cmd,
245 				s->data.process.sdata, &w));
246 			if (error)
247 				break;
248 			error = copyout(&w, s->data.process.rdata,
249 					sizeof(*(s->data.process.rdata)));
250 		}
251 
252 		break;
253 
254 	case SMB_BWRITE:
255 		if (s->count && s->data.byte_ptr) {
256 			if (s->count > SMB_MAXBLOCKSIZE)
257 				s->count = SMB_MAXBLOCKSIZE;
258 			error = copyin(s->data.byte_ptr, buf, s->count);
259 			if (error)
260 				break;
261 			error = smbus_error(smbus_bwrite(parent, s->slave,
262 						s->cmd, s->count, buf));
263 		}
264 		break;
265 
266 #if defined(COMPAT_FREEBSD4) || defined(COMPAT_FREEBSD5) || defined(COMPAT_FREEBSD6)
267 	case SMB_OLD_BREAD:
268 #endif
269 	case SMB_BREAD:
270 		if (s->count && s->data.byte_ptr) {
271 			count = min(s->count, SMB_MAXBLOCKSIZE);
272 			error = smbus_error(smbus_bread(parent, s->slave,
273 						s->cmd, &count, buf));
274 			if (error)
275 				break;
276 			error = copyout(buf, s->data.byte_ptr,
277 			    min(count, s->count));
278 			s->count = count;
279 		}
280 		break;
281 
282 	default:
283 		error = ENOTTY;
284 	}
285 
286 	smbus_release_bus(parent, smbdev);
287 
288 	return (error);
289 }
290 
291 DRIVER_MODULE(smb, smbus, smb_driver, smb_devclass, 0, 0);
292 MODULE_DEPEND(smb, smbus, SMBUS_MINVER, SMBUS_PREFVER, SMBUS_MAXVER);
293 MODULE_VERSION(smb, 1);
294