xref: /freebsd/sys/dev/smbus/smb.c (revision a05a680469a7ac77b195021fed74e3aa58152dd7)
1d70424edSNicolas Souchu /*-
24d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
3718cf2ccSPedro F. Giffuni  *
4c17d4340SNicolas Souchu  * Copyright (c) 1998, 2001 Nicolas Souchu
5376330acSStephen J. Kiernan  * Copyright (c) 2023 Juniper Networks, Inc.
6d70424edSNicolas Souchu  * All rights reserved.
7d70424edSNicolas Souchu  *
8d70424edSNicolas Souchu  * Redistribution and use in source and binary forms, with or without
9d70424edSNicolas Souchu  * modification, are permitted provided that the following conditions
10d70424edSNicolas Souchu  * are met:
11d70424edSNicolas Souchu  * 1. Redistributions of source code must retain the above copyright
12d70424edSNicolas Souchu  *    notice, this list of conditions and the following disclaimer.
13d70424edSNicolas Souchu  * 2. Redistributions in binary form must reproduce the above copyright
14d70424edSNicolas Souchu  *    notice, this list of conditions and the following disclaimer in the
15d70424edSNicolas Souchu  *    documentation and/or other materials provided with the distribution.
16d70424edSNicolas Souchu  *
17d70424edSNicolas Souchu  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18d70424edSNicolas Souchu  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19d70424edSNicolas Souchu  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20d70424edSNicolas Souchu  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21d70424edSNicolas Souchu  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22d70424edSNicolas Souchu  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23d70424edSNicolas Souchu  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24d70424edSNicolas Souchu  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25d70424edSNicolas Souchu  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26d70424edSNicolas Souchu  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27d70424edSNicolas Souchu  * SUCH DAMAGE.
28d70424edSNicolas Souchu  */
29b3dd2b7bSJoerg Wunsch 
30d70424edSNicolas Souchu #include <sys/param.h>
31d70424edSNicolas Souchu #include <sys/kernel.h>
32d70424edSNicolas Souchu #include <sys/systm.h>
33376330acSStephen J. Kiernan #include <sys/abi_compat.h>
34d70424edSNicolas Souchu #include <sys/module.h>
35d70424edSNicolas Souchu #include <sys/bus.h>
36d70424edSNicolas Souchu #include <sys/conf.h>
37d70424edSNicolas Souchu #include <sys/uio.h>
38ba81c311SNicolas Souchu #include <sys/fcntl.h>
39d70424edSNicolas Souchu 
40d70424edSNicolas Souchu #include <dev/smbus/smbconf.h>
41d70424edSNicolas Souchu #include <dev/smbus/smbus.h>
42531facbaSPeter Wemm #include <dev/smbus/smb.h>
43d70424edSNicolas Souchu 
44d70424edSNicolas Souchu #include "smbus_if.h"
45d70424edSNicolas Souchu 
46376330acSStephen J. Kiernan #ifdef COMPAT_FREEBSD32
47376330acSStephen J. Kiernan struct smbcmd32 {
48376330acSStephen J. Kiernan 	u_char cmd;
49376330acSStephen J. Kiernan 	u_char reserved;
50376330acSStephen J. Kiernan 	u_short op;
51376330acSStephen J. Kiernan 	union {
52376330acSStephen J. Kiernan 		char	byte;
53376330acSStephen J. Kiernan 		char	buf[2];
54376330acSStephen J. Kiernan 		short	word;
55376330acSStephen J. Kiernan 	} wdata;
56376330acSStephen J. Kiernan 	union {
57376330acSStephen J. Kiernan 		char	byte;
58376330acSStephen J. Kiernan 		char	buf[2];
59376330acSStephen J. Kiernan 		short	word;
60376330acSStephen J. Kiernan 	} rdata;
61376330acSStephen J. Kiernan 	int slave;
62376330acSStephen J. Kiernan 	uint32_t wbuf;
63376330acSStephen J. Kiernan 	int wcount;
64376330acSStephen J. Kiernan 	uint32_t rbuf;
65376330acSStephen J. Kiernan 	int rcount;
66376330acSStephen J. Kiernan };
67376330acSStephen J. Kiernan 
68376330acSStephen J. Kiernan #define	SMB_QUICK_WRITE32	_IOW('i', 1, struct smbcmd32)
69376330acSStephen J. Kiernan #define	SMB_QUICK_READ32	_IOW('i', 2, struct smbcmd32)
70376330acSStephen J. Kiernan #define	SMB_SENDB32		_IOW('i', 3, struct smbcmd32)
71376330acSStephen J. Kiernan #define	SMB_RECVB32		_IOWR('i', 4, struct smbcmd32)
72376330acSStephen J. Kiernan #define	SMB_WRITEB32		_IOW('i', 5, struct smbcmd32)
73376330acSStephen J. Kiernan #define	SMB_WRITEW32		_IOW('i', 6, struct smbcmd32)
74376330acSStephen J. Kiernan #define	SMB_READB32		_IOWR('i', 7, struct smbcmd32)
75376330acSStephen J. Kiernan #define	SMB_READW32		_IOWR('i', 8, struct smbcmd32)
76376330acSStephen J. Kiernan #define	SMB_PCALL32		_IOWR('i', 9, struct smbcmd32)
77376330acSStephen J. Kiernan #define	SMB_BWRITE32		_IOW('i', 10, struct smbcmd32)
78376330acSStephen J. Kiernan #define	SMB_BREAD32		_IOWR('i', 11, struct smbcmd32)
79376330acSStephen J. Kiernan #define	SMB_OLD_READB32		_IOW('i', 7, struct smbcmd32)
80376330acSStephen J. Kiernan #define	SMB_OLD_READW32		_IOW('i', 8, struct smbcmd32)
81376330acSStephen J. Kiernan #define	SMB_OLD_PCALL32		_IOW('i', 9, struct smbcmd32)
82376330acSStephen J. Kiernan #endif
83376330acSStephen J. Kiernan 
84f43618f5SAndriy Gapon #define SMB_OLD_READB	_IOW('i', 7, struct smbcmd)
85f43618f5SAndriy Gapon #define SMB_OLD_READW	_IOW('i', 8, struct smbcmd)
86f43618f5SAndriy Gapon #define SMB_OLD_PCALL	_IOW('i', 9, struct smbcmd)
87d70424edSNicolas Souchu 
88d70424edSNicolas Souchu struct smb_softc {
89697218c9SJohn Baldwin 	device_t sc_dev;
9089c9c53dSPoul-Henning Kamp 	struct cdev *sc_devnode;
91d70424edSNicolas Souchu };
92d70424edSNicolas Souchu 
937048a99cSJohn Baldwin static void smb_identify(driver_t *driver, device_t parent);
94d70424edSNicolas Souchu static int smb_probe(device_t);
95d70424edSNicolas Souchu static int smb_attach(device_t);
96c17d4340SNicolas Souchu static int smb_detach(device_t);
97d70424edSNicolas Souchu 
98d70424edSNicolas Souchu static device_method_t smb_methods[] = {
99d70424edSNicolas Souchu 	/* device interface */
1007048a99cSJohn Baldwin 	DEVMETHOD(device_identify,	smb_identify),
101d70424edSNicolas Souchu 	DEVMETHOD(device_probe,		smb_probe),
102d70424edSNicolas Souchu 	DEVMETHOD(device_attach,	smb_attach),
103c17d4340SNicolas Souchu 	DEVMETHOD(device_detach,	smb_detach),
104d70424edSNicolas Souchu 
105d70424edSNicolas Souchu 	/* smbus interface */
106d70424edSNicolas Souchu 	DEVMETHOD(smbus_intr,		smbus_generic_intr),
107d70424edSNicolas Souchu 	{ 0, 0 }
108d70424edSNicolas Souchu };
109d70424edSNicolas Souchu 
110d70424edSNicolas Souchu static driver_t smb_driver = {
111d70424edSNicolas Souchu 	"smb",
112d70424edSNicolas Souchu 	smb_methods,
113d70424edSNicolas Souchu 	sizeof(struct smb_softc),
114d70424edSNicolas Souchu };
115d70424edSNicolas Souchu 
116d70424edSNicolas Souchu static	d_ioctl_t	smbioctl;
117d70424edSNicolas Souchu 
1184e2f199eSPoul-Henning Kamp static struct cdevsw smb_cdevsw = {
119dc08ffecSPoul-Henning Kamp 	.d_version =	D_VERSION,
120697218c9SJohn Baldwin 	.d_flags =	D_TRACKCLOSE,
1217ac40f5fSPoul-Henning Kamp 	.d_ioctl =	smbioctl,
1227ac40f5fSPoul-Henning Kamp 	.d_name =	"smb",
1234e2f199eSPoul-Henning Kamp };
124d70424edSNicolas Souchu 
1257048a99cSJohn Baldwin static void
1267048a99cSJohn Baldwin smb_identify(driver_t *driver, device_t parent)
1277048a99cSJohn Baldwin {
1287048a99cSJohn Baldwin 
1297048a99cSJohn Baldwin 	if (device_find_child(parent, "smb", -1) == NULL)
130*a05a6804SWarner Losh 		BUS_ADD_CHILD(parent, 0, "smb", DEVICE_UNIT_ANY);
1317048a99cSJohn Baldwin }
1327048a99cSJohn Baldwin 
133d70424edSNicolas Souchu static int
134d70424edSNicolas Souchu smb_probe(device_t dev)
135d70424edSNicolas Souchu {
136202379afSMichael Gmelin 	if (smbus_get_addr(dev) != -1)
137202379afSMichael Gmelin 		return (ENXIO);
138d70424edSNicolas Souchu 
139202379afSMichael Gmelin 	device_set_desc(dev, "SMBus generic I/O");
140202379afSMichael Gmelin 	return (BUS_PROBE_NOWILDCARD);
141d70424edSNicolas Souchu }
142d70424edSNicolas Souchu 
143d70424edSNicolas Souchu static int
144d70424edSNicolas Souchu smb_attach(device_t dev)
145d70424edSNicolas Souchu {
14668b2efbdSKonstantin Belousov 	struct smb_softc *sc;
14768b2efbdSKonstantin Belousov 	struct make_dev_args mda;
14868b2efbdSKonstantin Belousov 	int error;
149c17d4340SNicolas Souchu 
15068b2efbdSKonstantin Belousov 	sc = device_get_softc(dev);
151697218c9SJohn Baldwin 	sc->sc_dev = dev;
152202379afSMichael Gmelin 
15368b2efbdSKonstantin Belousov 	make_dev_args_init(&mda);
15468b2efbdSKonstantin Belousov 	mda.mda_devsw = &smb_cdevsw;
15568b2efbdSKonstantin Belousov 	mda.mda_unit = device_get_unit(dev);
15668b2efbdSKonstantin Belousov 	mda.mda_uid = UID_ROOT;
15768b2efbdSKonstantin Belousov 	mda.mda_gid = GID_WHEEL;
15868b2efbdSKonstantin Belousov 	mda.mda_mode = 0600;
15968b2efbdSKonstantin Belousov 	mda.mda_si_drv1 = sc;
16068b2efbdSKonstantin Belousov 	error = make_dev_s(&mda, &sc->sc_devnode, "smb%d", mda.mda_unit);
16168b2efbdSKonstantin Belousov 	return (error);
162c17d4340SNicolas Souchu }
163c17d4340SNicolas Souchu 
164c17d4340SNicolas Souchu static int
165c17d4340SNicolas Souchu smb_detach(device_t dev)
166c17d4340SNicolas Souchu {
16768b2efbdSKonstantin Belousov 	struct smb_softc *sc;
168c17d4340SNicolas Souchu 
16968b2efbdSKonstantin Belousov 	sc = device_get_softc(dev);
170c17d4340SNicolas Souchu 	destroy_dev(sc->sc_devnode);
171d70424edSNicolas Souchu 	return (0);
172d70424edSNicolas Souchu }
173d70424edSNicolas Souchu 
174376330acSStephen J. Kiernan #ifdef COMPAT_FREEBSD32
175376330acSStephen J. Kiernan static void
176376330acSStephen J. Kiernan smbcopyincmd32(struct smbcmd32 *uaddr, struct smbcmd *kaddr)
177376330acSStephen J. Kiernan {
178376330acSStephen J. Kiernan 	CP(*uaddr, *kaddr, cmd);
179376330acSStephen J. Kiernan 	CP(*uaddr, *kaddr, op);
180376330acSStephen J. Kiernan 	CP(*uaddr, *kaddr, wdata.word);
181376330acSStephen J. Kiernan 	CP(*uaddr, *kaddr, slave);
182376330acSStephen J. Kiernan 	PTRIN_CP(*uaddr, *kaddr, wbuf);
183376330acSStephen J. Kiernan 	CP(*uaddr, *kaddr, wcount);
184376330acSStephen J. Kiernan 	PTRIN_CP(*uaddr, *kaddr, rbuf);
185376330acSStephen J. Kiernan 	CP(*uaddr, *kaddr, rcount);
186376330acSStephen J. Kiernan }
187376330acSStephen J. Kiernan #endif
188376330acSStephen J. Kiernan 
189d70424edSNicolas Souchu static int
19089c9c53dSPoul-Henning Kamp smbioctl(struct cdev *dev, u_long cmd, caddr_t data, int flags, struct thread *td)
191d70424edSNicolas Souchu {
192add37e1eSJoerg Wunsch 	char buf[SMB_MAXBLOCKSIZE];
193b3dd2b7bSJoerg Wunsch 	device_t parent;
194376330acSStephen J. Kiernan #ifdef COMPAT_FREEBSD32
195376330acSStephen J. Kiernan 	struct smbcmd sswab;
196376330acSStephen J. Kiernan 	struct smbcmd32 *s32 = (struct smbcmd32 *)data;
197376330acSStephen J. Kiernan #endif
198d70424edSNicolas Souchu 	struct smbcmd *s = (struct smbcmd *)data;
199697218c9SJohn Baldwin 	struct smb_softc *sc = dev->si_drv1;
200697218c9SJohn Baldwin 	device_t smbdev = sc->sc_dev;
201b3dd2b7bSJoerg Wunsch 	int error;
202202379afSMichael Gmelin 	int unit;
203202379afSMichael Gmelin 	u_char bcount;
204202379afSMichael Gmelin 
205202379afSMichael Gmelin 	/*
206202379afSMichael Gmelin 	 * If a specific slave device is being used, override any passed-in
207202379afSMichael Gmelin 	 * slave.
208202379afSMichael Gmelin 	 */
209202379afSMichael Gmelin 	unit = dev2unit(dev);
210202379afSMichael Gmelin 	if (unit & 0x0400)
211202379afSMichael Gmelin 		s->slave = unit & 0x03ff;
212d70424edSNicolas Souchu 
213b3dd2b7bSJoerg Wunsch 	parent = device_get_parent(smbdev);
214b3dd2b7bSJoerg Wunsch 
215bb6bb7feSJohn Baldwin 	/* Make sure that LSB bit is cleared. */
216bb6bb7feSJohn Baldwin 	if (s->slave & 0x1)
217bb6bb7feSJohn Baldwin 		return (EINVAL);
218bb6bb7feSJohn Baldwin 
219b3dd2b7bSJoerg Wunsch 	/* Allocate the bus. */
220ba81c311SNicolas Souchu 	if ((error = smbus_request_bus(parent, smbdev,
221ba81c311SNicolas Souchu 			(flags & O_NONBLOCK) ? SMB_DONTWAIT : (SMB_WAIT | SMB_INTR))))
222ba81c311SNicolas Souchu 		return (error);
223ba81c311SNicolas Souchu 
224376330acSStephen J. Kiernan #ifdef COMPAT_FREEBSD32
225376330acSStephen J. Kiernan 	switch (cmd) {
226376330acSStephen J. Kiernan 	case SMB_QUICK_WRITE32:
227376330acSStephen J. Kiernan 	case SMB_QUICK_READ32:
228376330acSStephen J. Kiernan 	case SMB_SENDB32:
229376330acSStephen J. Kiernan 	case SMB_RECVB32:
230376330acSStephen J. Kiernan 	case SMB_WRITEB32:
231376330acSStephen J. Kiernan 	case SMB_WRITEW32:
232376330acSStephen J. Kiernan 	case SMB_OLD_READB32:
233376330acSStephen J. Kiernan 	case SMB_READB32:
234376330acSStephen J. Kiernan 	case SMB_OLD_READW32:
235376330acSStephen J. Kiernan 	case SMB_READW32:
236376330acSStephen J. Kiernan 	case SMB_OLD_PCALL32:
237376330acSStephen J. Kiernan 	case SMB_PCALL32:
238376330acSStephen J. Kiernan 	case SMB_BWRITE32:
239376330acSStephen J. Kiernan 	case SMB_BREAD32:
240376330acSStephen J. Kiernan 		smbcopyincmd32(s32, &sswab);
241376330acSStephen J. Kiernan 		s = &sswab;
242376330acSStephen J. Kiernan 		break;
243376330acSStephen J. Kiernan 	default:
244376330acSStephen J. Kiernan 		break;
245376330acSStephen J. Kiernan 	}
246376330acSStephen J. Kiernan #endif
247376330acSStephen J. Kiernan 
248d70424edSNicolas Souchu 	switch (cmd) {
249d70424edSNicolas Souchu 	case SMB_QUICK_WRITE:
250376330acSStephen J. Kiernan #ifdef COMPAT_FREEBSD32
251376330acSStephen J. Kiernan 	case SMB_QUICK_WRITE32:
252376330acSStephen J. Kiernan #endif
2534012f363SNicolas Souchu 		error = smbus_error(smbus_quick(parent, s->slave, SMB_QWRITE));
254ba81c311SNicolas Souchu 		break;
255d70424edSNicolas Souchu 
256d70424edSNicolas Souchu 	case SMB_QUICK_READ:
257376330acSStephen J. Kiernan #ifdef COMPAT_FREEBSD32
258376330acSStephen J. Kiernan 	case SMB_QUICK_READ32:
259376330acSStephen J. Kiernan #endif
2604012f363SNicolas Souchu 		error = smbus_error(smbus_quick(parent, s->slave, SMB_QREAD));
261ba81c311SNicolas Souchu 		break;
262d70424edSNicolas Souchu 
263d70424edSNicolas Souchu 	case SMB_SENDB:
264376330acSStephen J. Kiernan #ifdef COMPAT_FREEBSD32
265376330acSStephen J. Kiernan 	case SMB_SENDB32:
266376330acSStephen J. Kiernan #endif
2674012f363SNicolas Souchu 		error = smbus_error(smbus_sendb(parent, s->slave, s->cmd));
268d70424edSNicolas Souchu 		break;
269d70424edSNicolas Souchu 
270d70424edSNicolas Souchu 	case SMB_RECVB:
271376330acSStephen J. Kiernan #ifdef COMPAT_FREEBSD32
272376330acSStephen J. Kiernan 	case SMB_RECVB32:
273376330acSStephen J. Kiernan #endif
2744012f363SNicolas Souchu 		error = smbus_error(smbus_recvb(parent, s->slave, &s->cmd));
275d70424edSNicolas Souchu 		break;
276d70424edSNicolas Souchu 
277d70424edSNicolas Souchu 	case SMB_WRITEB:
278376330acSStephen J. Kiernan #ifdef COMPAT_FREEBSD32
279376330acSStephen J. Kiernan 	case SMB_WRITEB32:
280376330acSStephen J. Kiernan #endif
2814012f363SNicolas Souchu 		error = smbus_error(smbus_writeb(parent, s->slave, s->cmd,
282202379afSMichael Gmelin 						s->wdata.byte));
283d70424edSNicolas Souchu 		break;
284d70424edSNicolas Souchu 
285d70424edSNicolas Souchu 	case SMB_WRITEW:
286376330acSStephen J. Kiernan #ifdef COMPAT_FREEBSD32
287376330acSStephen J. Kiernan 	case SMB_WRITEW32:
288376330acSStephen J. Kiernan #endif
2894012f363SNicolas Souchu 		error = smbus_error(smbus_writew(parent, s->slave,
290202379afSMichael Gmelin 						s->cmd, s->wdata.word));
291d70424edSNicolas Souchu 		break;
292d70424edSNicolas Souchu 
293f43618f5SAndriy Gapon 	case SMB_OLD_READB:
294d70424edSNicolas Souchu 	case SMB_READB:
295376330acSStephen J. Kiernan #ifdef COMPAT_FREEBSD32
296376330acSStephen J. Kiernan 	case SMB_OLD_READB32:
297376330acSStephen J. Kiernan 	case SMB_READB32:
298376330acSStephen J. Kiernan #endif
299f43618f5SAndriy Gapon 		/* NB: for SMB_OLD_READB the read data goes to rbuf only. */
300202379afSMichael Gmelin 		error = smbus_error(smbus_readb(parent, s->slave, s->cmd,
301202379afSMichael Gmelin 		    &s->rdata.byte));
302add37e1eSJoerg Wunsch 		if (error)
303add37e1eSJoerg Wunsch 			break;
304202379afSMichael Gmelin 		if (s->rbuf && s->rcount >= 1) {
305202379afSMichael Gmelin 			error = copyout(&s->rdata.byte, s->rbuf, 1);
306202379afSMichael Gmelin 			s->rcount = 1;
307add37e1eSJoerg Wunsch 		}
308d70424edSNicolas Souchu 		break;
309d70424edSNicolas Souchu 
310f43618f5SAndriy Gapon 	case SMB_OLD_READW:
311d70424edSNicolas Souchu 	case SMB_READW:
312376330acSStephen J. Kiernan #ifdef COMPAT_FREEBSD32
313376330acSStephen J. Kiernan 	case SMB_OLD_READW32:
314376330acSStephen J. Kiernan 	case SMB_READW32:
315376330acSStephen J. Kiernan #endif
316f43618f5SAndriy Gapon 		/* NB: for SMB_OLD_READW the read data goes to rbuf only. */
317202379afSMichael Gmelin 		error = smbus_error(smbus_readw(parent, s->slave, s->cmd,
318202379afSMichael Gmelin 		    &s->rdata.word));
319202379afSMichael Gmelin 		if (error)
320202379afSMichael Gmelin 			break;
321202379afSMichael Gmelin 		if (s->rbuf && s->rcount >= 2) {
322202379afSMichael Gmelin 			buf[0] = (u_char)s->rdata.word;
323202379afSMichael Gmelin 			buf[1] = (u_char)(s->rdata.word >> 8);
324202379afSMichael Gmelin 			error = copyout(buf, s->rbuf, 2);
325202379afSMichael Gmelin 			s->rcount = 2;
326add37e1eSJoerg Wunsch 		}
327d70424edSNicolas Souchu 		break;
328d70424edSNicolas Souchu 
329f43618f5SAndriy Gapon 	case SMB_OLD_PCALL:
330d70424edSNicolas Souchu 	case SMB_PCALL:
331376330acSStephen J. Kiernan #ifdef COMPAT_FREEBSD32
332376330acSStephen J. Kiernan 	case SMB_OLD_PCALL32:
333376330acSStephen J. Kiernan 	case SMB_PCALL32:
334376330acSStephen J. Kiernan #endif
335f43618f5SAndriy Gapon 		/* NB: for SMB_OLD_PCALL the read data goes to rbuf only. */
3364012f363SNicolas Souchu 		error = smbus_error(smbus_pcall(parent, s->slave, s->cmd,
337202379afSMichael Gmelin 		    s->wdata.word, &s->rdata.word));
338add37e1eSJoerg Wunsch 		if (error)
339add37e1eSJoerg Wunsch 			break;
340202379afSMichael Gmelin 		if (s->rbuf && s->rcount >= 2) {
341202379afSMichael Gmelin 			buf[0] = (u_char)s->rdata.word;
342202379afSMichael Gmelin 			buf[1] = (u_char)(s->rdata.word >> 8);
343202379afSMichael Gmelin 			error = copyout(buf, s->rbuf, 2);
344202379afSMichael Gmelin 			s->rcount = 2;
345add37e1eSJoerg Wunsch 		}
346add37e1eSJoerg Wunsch 
347d70424edSNicolas Souchu 		break;
348d70424edSNicolas Souchu 
349d70424edSNicolas Souchu 	case SMB_BWRITE:
350376330acSStephen J. Kiernan #ifdef COMPAT_FREEBSD32
351376330acSStephen J. Kiernan 	case SMB_BWRITE32:
352376330acSStephen J. Kiernan #endif
353202379afSMichael Gmelin 		if (s->wcount < 0) {
354202379afSMichael Gmelin 			error = EINVAL;
355202379afSMichael Gmelin 			break;
356202379afSMichael Gmelin 		}
357202379afSMichael Gmelin 		if (s->wcount > SMB_MAXBLOCKSIZE)
358202379afSMichael Gmelin 			s->wcount = SMB_MAXBLOCKSIZE;
359202379afSMichael Gmelin 		if (s->wcount)
360202379afSMichael Gmelin 			error = copyin(s->wbuf, buf, s->wcount);
361add37e1eSJoerg Wunsch 		if (error)
362add37e1eSJoerg Wunsch 			break;
363202379afSMichael Gmelin 		error = smbus_error(smbus_bwrite(parent, s->slave, s->cmd,
364202379afSMichael Gmelin 		    s->wcount, buf));
365d70424edSNicolas Souchu 		break;
366d70424edSNicolas Souchu 
367d70424edSNicolas Souchu 	case SMB_BREAD:
368376330acSStephen J. Kiernan #ifdef COMPAT_FREEBSD32
369376330acSStephen J. Kiernan 	case SMB_BREAD32:
370376330acSStephen J. Kiernan #endif
371202379afSMichael Gmelin 		if (s->rcount < 0) {
372202379afSMichael Gmelin 			error = EINVAL;
373202379afSMichael Gmelin 			break;
374202379afSMichael Gmelin 		}
375202379afSMichael Gmelin 		if (s->rcount > SMB_MAXBLOCKSIZE)
376202379afSMichael Gmelin 			s->rcount = SMB_MAXBLOCKSIZE;
377202379afSMichael Gmelin 		error = smbus_error(smbus_bread(parent, s->slave, s->cmd,
378202379afSMichael Gmelin 		    &bcount, buf));
379add37e1eSJoerg Wunsch 		if (error)
380add37e1eSJoerg Wunsch 			break;
381202379afSMichael Gmelin 		if (s->rcount > bcount)
382202379afSMichael Gmelin 			s->rcount = bcount;
383202379afSMichael Gmelin 		error = copyout(buf, s->rbuf, s->rcount);
384d70424edSNicolas Souchu 		break;
385d70424edSNicolas Souchu 
386d70424edSNicolas Souchu 	default:
387add37e1eSJoerg Wunsch 		error = ENOTTY;
388d70424edSNicolas Souchu 	}
389d70424edSNicolas Souchu 
390376330acSStephen J. Kiernan #ifdef COMPAT_FREEBSD32
391376330acSStephen J. Kiernan 	switch (cmd) {
392376330acSStephen J. Kiernan 	case SMB_RECVB32:
393376330acSStephen J. Kiernan 		CP(*s, *s32, cmd);
394376330acSStephen J. Kiernan 		break;
395376330acSStephen J. Kiernan 	case SMB_OLD_READB32:
396376330acSStephen J. Kiernan 	case SMB_READB32:
397376330acSStephen J. Kiernan 	case SMB_OLD_READW32:
398376330acSStephen J. Kiernan 	case SMB_READW32:
399376330acSStephen J. Kiernan 	case SMB_OLD_PCALL32:
400376330acSStephen J. Kiernan 	case SMB_PCALL32:
401376330acSStephen J. Kiernan 		CP(*s, *s32, rdata.word);
402376330acSStephen J. Kiernan 		break;
403376330acSStephen J. Kiernan 	case SMB_BREAD32:
404376330acSStephen J. Kiernan 		if (s->rbuf == NULL)
405376330acSStephen J. Kiernan 			CP(*s, *s32, rdata.word);
406376330acSStephen J. Kiernan 		CP(*s, *s32, rcount);
407376330acSStephen J. Kiernan 		break;
408376330acSStephen J. Kiernan 	default:
409376330acSStephen J. Kiernan 		break;
410376330acSStephen J. Kiernan 	}
411376330acSStephen J. Kiernan #endif
412376330acSStephen J. Kiernan 
413ba81c311SNicolas Souchu 	smbus_release_bus(parent, smbdev);
414ba81c311SNicolas Souchu 
415d70424edSNicolas Souchu 	return (error);
416d70424edSNicolas Souchu }
417d70424edSNicolas Souchu 
4188d161143SJohn Baldwin DRIVER_MODULE(smb, smbus, smb_driver, 0, 0);
419c17d4340SNicolas Souchu MODULE_DEPEND(smb, smbus, SMBUS_MINVER, SMBUS_PREFVER, SMBUS_MAXVER);
420c17d4340SNicolas Souchu MODULE_VERSION(smb, 1);
421