1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (c) 1998, 2001 Nicolas Souchu
5 * Copyright (c) 2023 Juniper Networks, Inc.
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 */
29
30 #include <sys/param.h>
31 #include <sys/kernel.h>
32 #include <sys/systm.h>
33 #include <sys/abi_compat.h>
34 #include <sys/module.h>
35 #include <sys/bus.h>
36 #include <sys/conf.h>
37 #include <sys/uio.h>
38 #include <sys/fcntl.h>
39
40 #include <dev/smbus/smbconf.h>
41 #include <dev/smbus/smbus.h>
42 #include <dev/smbus/smb.h>
43
44 #include "smbus_if.h"
45
46 #ifdef COMPAT_FREEBSD32
47 struct smbcmd32 {
48 u_char cmd;
49 u_char reserved;
50 u_short op;
51 union {
52 char byte;
53 char buf[2];
54 short word;
55 } wdata;
56 union {
57 char byte;
58 char buf[2];
59 short word;
60 } rdata;
61 int slave;
62 uint32_t wbuf;
63 int wcount;
64 uint32_t rbuf;
65 int rcount;
66 };
67
68 #define SMB_QUICK_WRITE32 _IOW('i', 1, struct smbcmd32)
69 #define SMB_QUICK_READ32 _IOW('i', 2, struct smbcmd32)
70 #define SMB_SENDB32 _IOW('i', 3, struct smbcmd32)
71 #define SMB_RECVB32 _IOWR('i', 4, struct smbcmd32)
72 #define SMB_WRITEB32 _IOW('i', 5, struct smbcmd32)
73 #define SMB_WRITEW32 _IOW('i', 6, struct smbcmd32)
74 #define SMB_READB32 _IOWR('i', 7, struct smbcmd32)
75 #define SMB_READW32 _IOWR('i', 8, struct smbcmd32)
76 #define SMB_PCALL32 _IOWR('i', 9, struct smbcmd32)
77 #define SMB_BWRITE32 _IOW('i', 10, struct smbcmd32)
78 #define SMB_BREAD32 _IOWR('i', 11, struct smbcmd32)
79 #define SMB_OLD_READB32 _IOW('i', 7, struct smbcmd32)
80 #define SMB_OLD_READW32 _IOW('i', 8, struct smbcmd32)
81 #define SMB_OLD_PCALL32 _IOW('i', 9, struct smbcmd32)
82 #endif
83
84 #define SMB_OLD_READB _IOW('i', 7, struct smbcmd)
85 #define SMB_OLD_READW _IOW('i', 8, struct smbcmd)
86 #define SMB_OLD_PCALL _IOW('i', 9, struct smbcmd)
87
88 struct smb_softc {
89 device_t sc_dev;
90 struct cdev *sc_devnode;
91 };
92
93 static void smb_identify(driver_t *driver, device_t parent);
94 static int smb_probe(device_t);
95 static int smb_attach(device_t);
96 static int smb_detach(device_t);
97
98 static device_method_t smb_methods[] = {
99 /* device interface */
100 DEVMETHOD(device_identify, smb_identify),
101 DEVMETHOD(device_probe, smb_probe),
102 DEVMETHOD(device_attach, smb_attach),
103 DEVMETHOD(device_detach, smb_detach),
104
105 /* smbus interface */
106 DEVMETHOD(smbus_intr, smbus_generic_intr),
107 { 0, 0 }
108 };
109
110 static driver_t smb_driver = {
111 "smb",
112 smb_methods,
113 sizeof(struct smb_softc),
114 };
115
116 static d_ioctl_t smbioctl;
117
118 static struct cdevsw smb_cdevsw = {
119 .d_version = D_VERSION,
120 .d_flags = D_TRACKCLOSE,
121 .d_ioctl = smbioctl,
122 .d_name = "smb",
123 };
124
125 static void
smb_identify(driver_t * driver,device_t parent)126 smb_identify(driver_t *driver, device_t parent)
127 {
128
129 if (device_find_child(parent, "smb", -1) == NULL)
130 BUS_ADD_CHILD(parent, 0, "smb", -1);
131 }
132
133 static int
smb_probe(device_t dev)134 smb_probe(device_t dev)
135 {
136 if (smbus_get_addr(dev) != -1)
137 return (ENXIO);
138
139 device_set_desc(dev, "SMBus generic I/O");
140 return (BUS_PROBE_NOWILDCARD);
141 }
142
143 static int
smb_attach(device_t dev)144 smb_attach(device_t dev)
145 {
146 struct smb_softc *sc;
147 struct make_dev_args mda;
148 int error;
149
150 sc = device_get_softc(dev);
151 sc->sc_dev = dev;
152
153 make_dev_args_init(&mda);
154 mda.mda_devsw = &smb_cdevsw;
155 mda.mda_unit = device_get_unit(dev);
156 mda.mda_uid = UID_ROOT;
157 mda.mda_gid = GID_WHEEL;
158 mda.mda_mode = 0600;
159 mda.mda_si_drv1 = sc;
160 error = make_dev_s(&mda, &sc->sc_devnode, "smb%d", mda.mda_unit);
161 return (error);
162 }
163
164 static int
smb_detach(device_t dev)165 smb_detach(device_t dev)
166 {
167 struct smb_softc *sc;
168
169 sc = device_get_softc(dev);
170 destroy_dev(sc->sc_devnode);
171 return (0);
172 }
173
174 #ifdef COMPAT_FREEBSD32
175 static void
smbcopyincmd32(struct smbcmd32 * uaddr,struct smbcmd * kaddr)176 smbcopyincmd32(struct smbcmd32 *uaddr, struct smbcmd *kaddr)
177 {
178 CP(*uaddr, *kaddr, cmd);
179 CP(*uaddr, *kaddr, op);
180 CP(*uaddr, *kaddr, wdata.word);
181 CP(*uaddr, *kaddr, slave);
182 PTRIN_CP(*uaddr, *kaddr, wbuf);
183 CP(*uaddr, *kaddr, wcount);
184 PTRIN_CP(*uaddr, *kaddr, rbuf);
185 CP(*uaddr, *kaddr, rcount);
186 }
187 #endif
188
189 static int
smbioctl(struct cdev * dev,u_long cmd,caddr_t data,int flags,struct thread * td)190 smbioctl(struct cdev *dev, u_long cmd, caddr_t data, int flags, struct thread *td)
191 {
192 char buf[SMB_MAXBLOCKSIZE];
193 device_t parent;
194 #ifdef COMPAT_FREEBSD32
195 struct smbcmd sswab;
196 struct smbcmd32 *s32 = (struct smbcmd32 *)data;
197 #endif
198 struct smbcmd *s = (struct smbcmd *)data;
199 struct smb_softc *sc = dev->si_drv1;
200 device_t smbdev = sc->sc_dev;
201 int error;
202 int unit;
203 u_char bcount;
204
205 /*
206 * If a specific slave device is being used, override any passed-in
207 * slave.
208 */
209 unit = dev2unit(dev);
210 if (unit & 0x0400)
211 s->slave = unit & 0x03ff;
212
213 parent = device_get_parent(smbdev);
214
215 /* Make sure that LSB bit is cleared. */
216 if (s->slave & 0x1)
217 return (EINVAL);
218
219 /* Allocate the bus. */
220 if ((error = smbus_request_bus(parent, smbdev,
221 (flags & O_NONBLOCK) ? SMB_DONTWAIT : (SMB_WAIT | SMB_INTR))))
222 return (error);
223
224 #ifdef COMPAT_FREEBSD32
225 switch (cmd) {
226 case SMB_QUICK_WRITE32:
227 case SMB_QUICK_READ32:
228 case SMB_SENDB32:
229 case SMB_RECVB32:
230 case SMB_WRITEB32:
231 case SMB_WRITEW32:
232 case SMB_OLD_READB32:
233 case SMB_READB32:
234 case SMB_OLD_READW32:
235 case SMB_READW32:
236 case SMB_OLD_PCALL32:
237 case SMB_PCALL32:
238 case SMB_BWRITE32:
239 case SMB_BREAD32:
240 smbcopyincmd32(s32, &sswab);
241 s = &sswab;
242 break;
243 default:
244 break;
245 }
246 #endif
247
248 switch (cmd) {
249 case SMB_QUICK_WRITE:
250 #ifdef COMPAT_FREEBSD32
251 case SMB_QUICK_WRITE32:
252 #endif
253 error = smbus_error(smbus_quick(parent, s->slave, SMB_QWRITE));
254 break;
255
256 case SMB_QUICK_READ:
257 #ifdef COMPAT_FREEBSD32
258 case SMB_QUICK_READ32:
259 #endif
260 error = smbus_error(smbus_quick(parent, s->slave, SMB_QREAD));
261 break;
262
263 case SMB_SENDB:
264 #ifdef COMPAT_FREEBSD32
265 case SMB_SENDB32:
266 #endif
267 error = smbus_error(smbus_sendb(parent, s->slave, s->cmd));
268 break;
269
270 case SMB_RECVB:
271 #ifdef COMPAT_FREEBSD32
272 case SMB_RECVB32:
273 #endif
274 error = smbus_error(smbus_recvb(parent, s->slave, &s->cmd));
275 break;
276
277 case SMB_WRITEB:
278 #ifdef COMPAT_FREEBSD32
279 case SMB_WRITEB32:
280 #endif
281 error = smbus_error(smbus_writeb(parent, s->slave, s->cmd,
282 s->wdata.byte));
283 break;
284
285 case SMB_WRITEW:
286 #ifdef COMPAT_FREEBSD32
287 case SMB_WRITEW32:
288 #endif
289 error = smbus_error(smbus_writew(parent, s->slave,
290 s->cmd, s->wdata.word));
291 break;
292
293 case SMB_OLD_READB:
294 case SMB_READB:
295 #ifdef COMPAT_FREEBSD32
296 case SMB_OLD_READB32:
297 case SMB_READB32:
298 #endif
299 /* NB: for SMB_OLD_READB the read data goes to rbuf only. */
300 error = smbus_error(smbus_readb(parent, s->slave, s->cmd,
301 &s->rdata.byte));
302 if (error)
303 break;
304 if (s->rbuf && s->rcount >= 1) {
305 error = copyout(&s->rdata.byte, s->rbuf, 1);
306 s->rcount = 1;
307 }
308 break;
309
310 case SMB_OLD_READW:
311 case SMB_READW:
312 #ifdef COMPAT_FREEBSD32
313 case SMB_OLD_READW32:
314 case SMB_READW32:
315 #endif
316 /* NB: for SMB_OLD_READW the read data goes to rbuf only. */
317 error = smbus_error(smbus_readw(parent, s->slave, s->cmd,
318 &s->rdata.word));
319 if (error)
320 break;
321 if (s->rbuf && s->rcount >= 2) {
322 buf[0] = (u_char)s->rdata.word;
323 buf[1] = (u_char)(s->rdata.word >> 8);
324 error = copyout(buf, s->rbuf, 2);
325 s->rcount = 2;
326 }
327 break;
328
329 case SMB_OLD_PCALL:
330 case SMB_PCALL:
331 #ifdef COMPAT_FREEBSD32
332 case SMB_OLD_PCALL32:
333 case SMB_PCALL32:
334 #endif
335 /* NB: for SMB_OLD_PCALL the read data goes to rbuf only. */
336 error = smbus_error(smbus_pcall(parent, s->slave, s->cmd,
337 s->wdata.word, &s->rdata.word));
338 if (error)
339 break;
340 if (s->rbuf && s->rcount >= 2) {
341 buf[0] = (u_char)s->rdata.word;
342 buf[1] = (u_char)(s->rdata.word >> 8);
343 error = copyout(buf, s->rbuf, 2);
344 s->rcount = 2;
345 }
346
347 break;
348
349 case SMB_BWRITE:
350 #ifdef COMPAT_FREEBSD32
351 case SMB_BWRITE32:
352 #endif
353 if (s->wcount < 0) {
354 error = EINVAL;
355 break;
356 }
357 if (s->wcount > SMB_MAXBLOCKSIZE)
358 s->wcount = SMB_MAXBLOCKSIZE;
359 if (s->wcount)
360 error = copyin(s->wbuf, buf, s->wcount);
361 if (error)
362 break;
363 error = smbus_error(smbus_bwrite(parent, s->slave, s->cmd,
364 s->wcount, buf));
365 break;
366
367 case SMB_BREAD:
368 #ifdef COMPAT_FREEBSD32
369 case SMB_BREAD32:
370 #endif
371 if (s->rcount < 0) {
372 error = EINVAL;
373 break;
374 }
375 if (s->rcount > SMB_MAXBLOCKSIZE)
376 s->rcount = SMB_MAXBLOCKSIZE;
377 error = smbus_error(smbus_bread(parent, s->slave, s->cmd,
378 &bcount, buf));
379 if (error)
380 break;
381 if (s->rcount > bcount)
382 s->rcount = bcount;
383 error = copyout(buf, s->rbuf, s->rcount);
384 break;
385
386 default:
387 error = ENOTTY;
388 }
389
390 #ifdef COMPAT_FREEBSD32
391 switch (cmd) {
392 case SMB_RECVB32:
393 CP(*s, *s32, cmd);
394 break;
395 case SMB_OLD_READB32:
396 case SMB_READB32:
397 case SMB_OLD_READW32:
398 case SMB_READW32:
399 case SMB_OLD_PCALL32:
400 case SMB_PCALL32:
401 CP(*s, *s32, rdata.word);
402 break;
403 case SMB_BREAD32:
404 if (s->rbuf == NULL)
405 CP(*s, *s32, rdata.word);
406 CP(*s, *s32, rcount);
407 break;
408 default:
409 break;
410 }
411 #endif
412
413 smbus_release_bus(parent, smbdev);
414
415 return (error);
416 }
417
418 DRIVER_MODULE(smb, smbus, smb_driver, 0, 0);
419 MODULE_DEPEND(smb, smbus, SMBUS_MINVER, SMBUS_PREFVER, SMBUS_MAXVER);
420 MODULE_VERSION(smb, 1);
421