xref: /dflybsd-src/sys/dev/smbus/smb/smb.c (revision 8d1ea4cc63e8cedbe8f670822d4606adf2cf7d45)
1*6ba12bf3SMatthew Dillon /*-
2*6ba12bf3SMatthew Dillon  * Copyright (c) 1998, 2001 Nicolas Souchu
3*6ba12bf3SMatthew Dillon  * All rights reserved.
4*6ba12bf3SMatthew Dillon  *
5*6ba12bf3SMatthew Dillon  * Redistribution and use in source and binary forms, with or without
6*6ba12bf3SMatthew Dillon  * modification, are permitted provided that the following conditions
7*6ba12bf3SMatthew Dillon  * are met:
8*6ba12bf3SMatthew Dillon  * 1. Redistributions of source code must retain the above copyright
9*6ba12bf3SMatthew Dillon  *    notice, this list of conditions and the following disclaimer.
10*6ba12bf3SMatthew Dillon  * 2. Redistributions in binary form must reproduce the above copyright
11*6ba12bf3SMatthew Dillon  *    notice, this list of conditions and the following disclaimer in the
12*6ba12bf3SMatthew Dillon  *    documentation and/or other materials provided with the distribution.
13*6ba12bf3SMatthew Dillon  *
14*6ba12bf3SMatthew Dillon  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15*6ba12bf3SMatthew Dillon  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16*6ba12bf3SMatthew Dillon  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17*6ba12bf3SMatthew Dillon  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18*6ba12bf3SMatthew Dillon  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19*6ba12bf3SMatthew Dillon  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20*6ba12bf3SMatthew Dillon  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21*6ba12bf3SMatthew Dillon  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22*6ba12bf3SMatthew Dillon  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23*6ba12bf3SMatthew Dillon  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24*6ba12bf3SMatthew Dillon  * SUCH DAMAGE.
25*6ba12bf3SMatthew Dillon  *
26*6ba12bf3SMatthew Dillon  * $FreeBSD: src/sys/dev/smbus/smb.c,v 1.34.8.2 2006/09/22 19:19:16 jhb Exp $
27*6ba12bf3SMatthew Dillon  *
28*6ba12bf3SMatthew Dillon  */
29*6ba12bf3SMatthew Dillon 
30*6ba12bf3SMatthew Dillon #include <sys/param.h>
31*6ba12bf3SMatthew Dillon #include <sys/kernel.h>
32*6ba12bf3SMatthew Dillon #include <sys/systm.h>
33*6ba12bf3SMatthew Dillon #include <sys/device.h>
34*6ba12bf3SMatthew Dillon #include <sys/module.h>
35*6ba12bf3SMatthew Dillon #include <sys/bus.h>
36*6ba12bf3SMatthew Dillon #include <sys/conf.h>
37*6ba12bf3SMatthew Dillon #include <sys/uio.h>
38*6ba12bf3SMatthew Dillon #include <sys/fcntl.h>
39*6ba12bf3SMatthew Dillon 
40*6ba12bf3SMatthew Dillon #include <bus/smbus/smbconf.h>
41*6ba12bf3SMatthew Dillon #include <bus/smbus/smbus.h>
42*6ba12bf3SMatthew Dillon #include "smb.h"
43*6ba12bf3SMatthew Dillon 
44*6ba12bf3SMatthew Dillon #include "smbus_if.h"
45*6ba12bf3SMatthew Dillon 
46*6ba12bf3SMatthew Dillon #define BUFSIZE 1024
47*6ba12bf3SMatthew Dillon 
48*6ba12bf3SMatthew Dillon struct smb_softc {
49*6ba12bf3SMatthew Dillon 	device_t sc_dev;
50*6ba12bf3SMatthew Dillon 	int sc_count;			/* >0 if device opened */
51*6ba12bf3SMatthew Dillon 	int sc_unit;
52*6ba12bf3SMatthew Dillon 	cdev_t sc_devnode;
53*6ba12bf3SMatthew Dillon };
54*6ba12bf3SMatthew Dillon 
55*6ba12bf3SMatthew Dillon #define SMB_SOFTC(unit) \
56*6ba12bf3SMatthew Dillon 	((struct smb_softc *)devclass_get_softc(smb_devclass, (unit)))
57*6ba12bf3SMatthew Dillon 
58*6ba12bf3SMatthew Dillon #define SMB_DEVICE(unit) \
59*6ba12bf3SMatthew Dillon 	(devclass_get_device(smb_devclass, (unit)))
60*6ba12bf3SMatthew Dillon 
61*6ba12bf3SMatthew Dillon static void smb_identify(driver_t *driver, device_t parent);
62*6ba12bf3SMatthew Dillon static int smb_probe(device_t);
63*6ba12bf3SMatthew Dillon static int smb_attach(device_t);
64*6ba12bf3SMatthew Dillon static int smb_detach(device_t);
65*6ba12bf3SMatthew Dillon 
66*6ba12bf3SMatthew Dillon static devclass_t smb_devclass;
67*6ba12bf3SMatthew Dillon 
68*6ba12bf3SMatthew Dillon static device_method_t smb_methods[] = {
69*6ba12bf3SMatthew Dillon 	/* device interface */
70*6ba12bf3SMatthew Dillon 	DEVMETHOD(device_identify,	smb_identify),
71*6ba12bf3SMatthew Dillon 	DEVMETHOD(device_probe,		smb_probe),
72*6ba12bf3SMatthew Dillon 	DEVMETHOD(device_attach,	smb_attach),
73*6ba12bf3SMatthew Dillon 	DEVMETHOD(device_detach,	smb_detach),
74*6ba12bf3SMatthew Dillon 
75*6ba12bf3SMatthew Dillon #if 0
76*6ba12bf3SMatthew Dillon 	/* bus interface */
77*6ba12bf3SMatthew Dillon 	DEVMETHOD(bus_driver_added,     bus_generic_driver_added),
78*6ba12bf3SMatthew Dillon 	DEVMETHOD(bus_print_child,      bus_generic_print_child),
79*6ba12bf3SMatthew Dillon #endif
80*6ba12bf3SMatthew Dillon 
81*6ba12bf3SMatthew Dillon 	/* smbus interface */
82*6ba12bf3SMatthew Dillon 	DEVMETHOD(smbus_intr,		smbus_generic_intr),
83*6ba12bf3SMatthew Dillon 
84*6ba12bf3SMatthew Dillon 	DEVMETHOD_END
85*6ba12bf3SMatthew Dillon };
86*6ba12bf3SMatthew Dillon 
87*6ba12bf3SMatthew Dillon static driver_t smb_driver = {
88*6ba12bf3SMatthew Dillon 	"smb",
89*6ba12bf3SMatthew Dillon 	smb_methods,
90*6ba12bf3SMatthew Dillon 	sizeof(struct smb_softc),
91*6ba12bf3SMatthew Dillon };
92*6ba12bf3SMatthew Dillon 
93*6ba12bf3SMatthew Dillon static	d_open_t	smbopen;
94*6ba12bf3SMatthew Dillon static	d_close_t	smbclose;
95*6ba12bf3SMatthew Dillon static	d_ioctl_t	smbioctl;
96*6ba12bf3SMatthew Dillon 
97*6ba12bf3SMatthew Dillon static struct dev_ops smb_ops = {
98*6ba12bf3SMatthew Dillon 	{ "smb", 0, 0 },
99*6ba12bf3SMatthew Dillon 	.d_open =	smbopen,
100*6ba12bf3SMatthew Dillon 	.d_close =	smbclose,
101*6ba12bf3SMatthew Dillon 	.d_ioctl =	smbioctl,
102*6ba12bf3SMatthew Dillon };
103*6ba12bf3SMatthew Dillon 
104*6ba12bf3SMatthew Dillon static void
smb_identify(driver_t * driver,device_t parent)105*6ba12bf3SMatthew Dillon smb_identify(driver_t *driver, device_t parent)
106*6ba12bf3SMatthew Dillon {
107*6ba12bf3SMatthew Dillon 	if (device_find_child(parent, "smb", -1) == NULL)
108*6ba12bf3SMatthew Dillon 		BUS_ADD_CHILD(parent, parent, 0, "smb", -1);
109*6ba12bf3SMatthew Dillon }
110*6ba12bf3SMatthew Dillon 
111*6ba12bf3SMatthew Dillon static int
smb_probe(device_t dev)112*6ba12bf3SMatthew Dillon smb_probe(device_t dev)
113*6ba12bf3SMatthew Dillon {
114*6ba12bf3SMatthew Dillon 	device_set_desc(dev, "SMBus generic I/O");
115*6ba12bf3SMatthew Dillon 
116*6ba12bf3SMatthew Dillon 	/* Allow other subclasses to override this driver. */
117*6ba12bf3SMatthew Dillon 	return (BUS_PROBE_GENERIC);
118*6ba12bf3SMatthew Dillon }
119*6ba12bf3SMatthew Dillon 
120*6ba12bf3SMatthew Dillon static int
smb_attach(device_t dev)121*6ba12bf3SMatthew Dillon smb_attach(device_t dev)
122*6ba12bf3SMatthew Dillon {
123*6ba12bf3SMatthew Dillon 	struct smb_softc *sc = (struct smb_softc *)device_get_softc(dev);
124*6ba12bf3SMatthew Dillon 	int unit;
125*6ba12bf3SMatthew Dillon 
126*6ba12bf3SMatthew Dillon 	if (!sc)
127*6ba12bf3SMatthew Dillon 		return (ENOMEM);
128*6ba12bf3SMatthew Dillon 
129*6ba12bf3SMatthew Dillon 	bzero(sc, sizeof(struct smb_softc *));
130*6ba12bf3SMatthew Dillon 	unit = device_get_unit(dev);
131*6ba12bf3SMatthew Dillon 	sc->sc_dev = dev;
132*6ba12bf3SMatthew Dillon 	sc->sc_unit = unit;
133*6ba12bf3SMatthew Dillon 
134*6ba12bf3SMatthew Dillon 	if (unit & 0x0400) {
135*6ba12bf3SMatthew Dillon 		sc->sc_devnode = make_dev(&smb_ops, unit,
136*6ba12bf3SMatthew Dillon 				UID_ROOT, GID_WHEEL, 0600,
137*6ba12bf3SMatthew Dillon 				"smb%d-%02x", unit >> 11, unit & 1023);
138*6ba12bf3SMatthew Dillon 	} else {
139*6ba12bf3SMatthew Dillon 		sc->sc_devnode = make_dev(&smb_ops, unit,
140*6ba12bf3SMatthew Dillon 				UID_ROOT, GID_WHEEL, 0600, "smb%d", unit);
141*6ba12bf3SMatthew Dillon 	}
142*6ba12bf3SMatthew Dillon 
143*6ba12bf3SMatthew Dillon 	return (0);
144*6ba12bf3SMatthew Dillon }
145*6ba12bf3SMatthew Dillon 
146*6ba12bf3SMatthew Dillon static int
smb_detach(device_t dev)147*6ba12bf3SMatthew Dillon smb_detach(device_t dev)
148*6ba12bf3SMatthew Dillon {
149*6ba12bf3SMatthew Dillon 	struct smb_softc *sc = (struct smb_softc *)device_get_softc(dev);
150*6ba12bf3SMatthew Dillon 
151*6ba12bf3SMatthew Dillon 	if (sc->sc_devnode)
152*6ba12bf3SMatthew Dillon 		dev_ops_remove_minor(&smb_ops, device_get_unit(dev));
153*6ba12bf3SMatthew Dillon 
154*6ba12bf3SMatthew Dillon 	return (0);
155*6ba12bf3SMatthew Dillon }
156*6ba12bf3SMatthew Dillon 
157*6ba12bf3SMatthew Dillon static int
smbopen(struct dev_open_args * ap)158*6ba12bf3SMatthew Dillon smbopen (struct dev_open_args *ap)
159*6ba12bf3SMatthew Dillon {
160*6ba12bf3SMatthew Dillon 	cdev_t dev = ap->a_head.a_dev;
161*6ba12bf3SMatthew Dillon 	struct smb_softc *sc = SMB_SOFTC(minor(dev));
162*6ba12bf3SMatthew Dillon 
163*6ba12bf3SMatthew Dillon 	if (sc == NULL)
164*6ba12bf3SMatthew Dillon 		return (ENXIO);
165*6ba12bf3SMatthew Dillon 
166*6ba12bf3SMatthew Dillon 	if (sc->sc_count != 0)
167*6ba12bf3SMatthew Dillon 		return (EBUSY);
168*6ba12bf3SMatthew Dillon 
169*6ba12bf3SMatthew Dillon 	sc->sc_count++;
170*6ba12bf3SMatthew Dillon 
171*6ba12bf3SMatthew Dillon 	return (0);
172*6ba12bf3SMatthew Dillon }
173*6ba12bf3SMatthew Dillon 
174*6ba12bf3SMatthew Dillon static int
smbclose(struct dev_close_args * ap)175*6ba12bf3SMatthew Dillon smbclose(struct dev_close_args *ap)
176*6ba12bf3SMatthew Dillon {
177*6ba12bf3SMatthew Dillon 	cdev_t dev = ap->a_head.a_dev;
178*6ba12bf3SMatthew Dillon 	struct smb_softc *sc = SMB_SOFTC(minor(dev));
179*6ba12bf3SMatthew Dillon 
180*6ba12bf3SMatthew Dillon 	if (sc == NULL)
181*6ba12bf3SMatthew Dillon 		return (ENXIO);
182*6ba12bf3SMatthew Dillon 
183*6ba12bf3SMatthew Dillon 	if (sc->sc_count == 0)
184*6ba12bf3SMatthew Dillon 		/* This is not supposed to happen. */
185*6ba12bf3SMatthew Dillon 		return (0);
186*6ba12bf3SMatthew Dillon 
187*6ba12bf3SMatthew Dillon 	sc->sc_count--;
188*6ba12bf3SMatthew Dillon 
189*6ba12bf3SMatthew Dillon 	return (0);
190*6ba12bf3SMatthew Dillon }
191*6ba12bf3SMatthew Dillon 
192*6ba12bf3SMatthew Dillon #if 0
193*6ba12bf3SMatthew Dillon static int
194*6ba12bf3SMatthew Dillon smbwrite(struct dev_write_args *ap)
195*6ba12bf3SMatthew Dillon {
196*6ba12bf3SMatthew Dillon 	return (EINVAL);
197*6ba12bf3SMatthew Dillon }
198*6ba12bf3SMatthew Dillon 
199*6ba12bf3SMatthew Dillon static int
200*6ba12bf3SMatthew Dillon smbread(struct dev_read_args *ap)
201*6ba12bf3SMatthew Dillon {
202*6ba12bf3SMatthew Dillon 	return (EINVAL);
203*6ba12bf3SMatthew Dillon }
204*6ba12bf3SMatthew Dillon #endif
205*6ba12bf3SMatthew Dillon 
206*6ba12bf3SMatthew Dillon static int
smbioctl(struct dev_ioctl_args * ap)207*6ba12bf3SMatthew Dillon smbioctl(struct dev_ioctl_args *ap)
208*6ba12bf3SMatthew Dillon {
209*6ba12bf3SMatthew Dillon 	cdev_t dev = ap->a_head.a_dev;
210*6ba12bf3SMatthew Dillon 	device_t bus;		/* smbbus */
211*6ba12bf3SMatthew Dillon 	char buf[SMB_MAXBLOCKSIZE];
212*6ba12bf3SMatthew Dillon 	struct smbcmd *s = (struct smbcmd *)ap->a_data;
213*6ba12bf3SMatthew Dillon 	struct smb_softc *sc = SMB_SOFTC(minor(dev));
214*6ba12bf3SMatthew Dillon 	device_t smbdev = SMB_DEVICE(minor(dev));
215*6ba12bf3SMatthew Dillon 	int error;
216*6ba12bf3SMatthew Dillon 	int unit;
217*6ba12bf3SMatthew Dillon 	u_char bcount;
218*6ba12bf3SMatthew Dillon 
219*6ba12bf3SMatthew Dillon 	if (sc == NULL)
220*6ba12bf3SMatthew Dillon 		return (ENXIO);
221*6ba12bf3SMatthew Dillon 	if (s == NULL)
222*6ba12bf3SMatthew Dillon 		return (EINVAL);
223*6ba12bf3SMatthew Dillon 
224*6ba12bf3SMatthew Dillon 	/*
225*6ba12bf3SMatthew Dillon 	 * If a specific slave device is being used, override any passed-in
226*6ba12bf3SMatthew Dillon 	 * slave.
227*6ba12bf3SMatthew Dillon 	 */
228*6ba12bf3SMatthew Dillon 	unit = sc->sc_unit;
229*6ba12bf3SMatthew Dillon 	if (unit & 0x0400) {
230*6ba12bf3SMatthew Dillon 		s->slave = unit & 1023;
231*6ba12bf3SMatthew Dillon 	}
232*6ba12bf3SMatthew Dillon 
233*6ba12bf3SMatthew Dillon 	/*
234*6ba12bf3SMatthew Dillon 	 * NOTE: smbus_*() functions automatically recurse the parent to
235*6ba12bf3SMatthew Dillon 	 *	 get to the actual device driver.
236*6ba12bf3SMatthew Dillon 	 */
237*6ba12bf3SMatthew Dillon 	bus = device_get_parent(smbdev);	/* smbus */
238*6ba12bf3SMatthew Dillon 
239*6ba12bf3SMatthew Dillon 	/* Allocate the bus. */
240*6ba12bf3SMatthew Dillon 	if ((error = smbus_request_bus(bus, smbdev,
241*6ba12bf3SMatthew Dillon 			(ap->a_fflag & O_NONBLOCK) ?
242*6ba12bf3SMatthew Dillon 			SMB_DONTWAIT : (SMB_WAIT | SMB_INTR))))
243*6ba12bf3SMatthew Dillon 		return (error);
244*6ba12bf3SMatthew Dillon 
245*6ba12bf3SMatthew Dillon 	switch (ap->a_cmd) {
246*6ba12bf3SMatthew Dillon 	case SMB_QUICK_WRITE:
247*6ba12bf3SMatthew Dillon 		error = smbus_error(smbus_quick(bus, s->slave, SMB_QWRITE));
248*6ba12bf3SMatthew Dillon 		break;
249*6ba12bf3SMatthew Dillon 
250*6ba12bf3SMatthew Dillon 	case SMB_QUICK_READ:
251*6ba12bf3SMatthew Dillon 		error = smbus_error(smbus_quick(bus, s->slave, SMB_QREAD));
252*6ba12bf3SMatthew Dillon 		break;
253*6ba12bf3SMatthew Dillon 
254*6ba12bf3SMatthew Dillon 	case SMB_SENDB:
255*6ba12bf3SMatthew Dillon 		error = smbus_error(smbus_sendb(bus, s->slave, s->cmd));
256*6ba12bf3SMatthew Dillon 		break;
257*6ba12bf3SMatthew Dillon 
258*6ba12bf3SMatthew Dillon 	case SMB_RECVB:
259*6ba12bf3SMatthew Dillon 		error = smbus_error(smbus_recvb(bus, s->slave, &s->cmd));
260*6ba12bf3SMatthew Dillon 		break;
261*6ba12bf3SMatthew Dillon 
262*6ba12bf3SMatthew Dillon 	case SMB_WRITEB:
263*6ba12bf3SMatthew Dillon 		error = smbus_error(smbus_writeb(bus, s->slave, s->cmd,
264*6ba12bf3SMatthew Dillon 						s->wdata.byte));
265*6ba12bf3SMatthew Dillon 		break;
266*6ba12bf3SMatthew Dillon 
267*6ba12bf3SMatthew Dillon 	case SMB_WRITEW:
268*6ba12bf3SMatthew Dillon 		error = smbus_error(smbus_writew(bus, s->slave, s->cmd,
269*6ba12bf3SMatthew Dillon 						s->wdata.word));
270*6ba12bf3SMatthew Dillon 		break;
271*6ba12bf3SMatthew Dillon 
272*6ba12bf3SMatthew Dillon 	case SMB_READB:
273*6ba12bf3SMatthew Dillon 		error = smbus_error(smbus_readb(bus, s->slave, s->cmd,
274*6ba12bf3SMatthew Dillon 						&s->rdata.byte));
275*6ba12bf3SMatthew Dillon 		if (s->rbuf && s->rcount >= 1) {
276*6ba12bf3SMatthew Dillon 			error = copyout(&s->rdata.byte, s->rbuf, 1);
277*6ba12bf3SMatthew Dillon 			s->rcount = 1;
278*6ba12bf3SMatthew Dillon 		}
279*6ba12bf3SMatthew Dillon 		break;
280*6ba12bf3SMatthew Dillon 
281*6ba12bf3SMatthew Dillon 	case SMB_READW:
282*6ba12bf3SMatthew Dillon 		error = smbus_error(smbus_readw(bus, s->slave, s->cmd,
283*6ba12bf3SMatthew Dillon 						&s->rdata.word));
284*6ba12bf3SMatthew Dillon 		if (s->rbuf && s->rcount >= 2) {
285*6ba12bf3SMatthew Dillon 			buf[0] = (u_char)s->rdata.word;
286*6ba12bf3SMatthew Dillon 			buf[1] = (u_char)(s->rdata.word >> 8);
287*6ba12bf3SMatthew Dillon 			error = copyout(buf, s->rbuf, 2);
288*6ba12bf3SMatthew Dillon 			s->rcount = 2;
289*6ba12bf3SMatthew Dillon 		}
290*6ba12bf3SMatthew Dillon 		break;
291*6ba12bf3SMatthew Dillon 
292*6ba12bf3SMatthew Dillon 	case SMB_PCALL:
293*6ba12bf3SMatthew Dillon 		error = smbus_error(smbus_pcall(bus, s->slave, s->cmd,
294*6ba12bf3SMatthew Dillon 						s->wdata.word, &s->rdata.word));
295*6ba12bf3SMatthew Dillon 		if (s->rbuf && s->rcount >= 2) {
296*6ba12bf3SMatthew Dillon 			char buf[2];
297*6ba12bf3SMatthew Dillon 			buf[0] = (u_char)s->rdata.word;
298*6ba12bf3SMatthew Dillon 			buf[1] = (u_char)(s->rdata.word >> 8);
299*6ba12bf3SMatthew Dillon 			error = copyout(buf, s->rbuf, 2);
300*6ba12bf3SMatthew Dillon 			s->rcount = 2;
301*6ba12bf3SMatthew Dillon 		}
302*6ba12bf3SMatthew Dillon 
303*6ba12bf3SMatthew Dillon 		break;
304*6ba12bf3SMatthew Dillon 
305*6ba12bf3SMatthew Dillon 	case SMB_BWRITE:
306*6ba12bf3SMatthew Dillon 		if (s->wcount < 0)
307*6ba12bf3SMatthew Dillon 			s->wcount = 0;
308*6ba12bf3SMatthew Dillon 		if (s->wcount > SMB_MAXBLOCKSIZE)
309*6ba12bf3SMatthew Dillon 			s->wcount = SMB_MAXBLOCKSIZE;
310*6ba12bf3SMatthew Dillon 		if (s->wcount)
311*6ba12bf3SMatthew Dillon 			error = copyin(s->wbuf, buf, s->wcount);
312*6ba12bf3SMatthew Dillon 		if (error)
313*6ba12bf3SMatthew Dillon 			break;
314*6ba12bf3SMatthew Dillon 		error = smbus_error(smbus_bwrite(bus, s->slave, s->cmd,
315*6ba12bf3SMatthew Dillon 						 s->wcount, buf));
316*6ba12bf3SMatthew Dillon 		break;
317*6ba12bf3SMatthew Dillon 
318*6ba12bf3SMatthew Dillon 	case SMB_BREAD:
319*6ba12bf3SMatthew Dillon 		if (s->rcount < 0)
320*6ba12bf3SMatthew Dillon 			s->rcount = 0;
321*6ba12bf3SMatthew Dillon 		if (s->rcount > SMB_MAXBLOCKSIZE)
322*6ba12bf3SMatthew Dillon 			s->rcount = SMB_MAXBLOCKSIZE;
323*6ba12bf3SMatthew Dillon 		error = smbus_bread(bus, s->slave, s->cmd, &bcount, buf);
324*6ba12bf3SMatthew Dillon 		error = smbus_error(error);
325*6ba12bf3SMatthew Dillon 		if (error)
326*6ba12bf3SMatthew Dillon 			break;
327*6ba12bf3SMatthew Dillon 		if (s->rcount > bcount)
328*6ba12bf3SMatthew Dillon 			s->rcount = bcount;
329*6ba12bf3SMatthew Dillon 		error = copyout(buf, s->rbuf, s->rcount);
330*6ba12bf3SMatthew Dillon 		break;
331*6ba12bf3SMatthew Dillon 
332*6ba12bf3SMatthew Dillon 	case SMB_TRANS:
333*6ba12bf3SMatthew Dillon 		if (s->rcount < 0)
334*6ba12bf3SMatthew Dillon 			s->rcount = 0;
335*6ba12bf3SMatthew Dillon 		if (s->rcount > SMB_MAXBLOCKSIZE)
336*6ba12bf3SMatthew Dillon 			s->rcount = SMB_MAXBLOCKSIZE;
337*6ba12bf3SMatthew Dillon 		if (s->wcount < 0)
338*6ba12bf3SMatthew Dillon 			s->wcount = 0;
339*6ba12bf3SMatthew Dillon 		if (s->wcount > SMB_MAXBLOCKSIZE)
340*6ba12bf3SMatthew Dillon 			s->wcount = SMB_MAXBLOCKSIZE;
341*6ba12bf3SMatthew Dillon 		if (s->wcount)
342*6ba12bf3SMatthew Dillon 			error = copyin(s->wbuf, buf, s->wcount);
343*6ba12bf3SMatthew Dillon 		if (error)
344*6ba12bf3SMatthew Dillon 			break;
345*6ba12bf3SMatthew Dillon 		error = smbus_trans(bus, s->slave, s->cmd, s->op,
346*6ba12bf3SMatthew Dillon 				    buf, s->wcount, buf, s->rcount, &s->rcount);
347*6ba12bf3SMatthew Dillon 		error = smbus_error(error);
348*6ba12bf3SMatthew Dillon 		if (error == 0)
349*6ba12bf3SMatthew Dillon 			error = copyout(buf, s->rbuf, s->rcount);
350*6ba12bf3SMatthew Dillon 		break;
351*6ba12bf3SMatthew Dillon 	default:
352*6ba12bf3SMatthew Dillon 		error = ENOTTY;
353*6ba12bf3SMatthew Dillon 		break;
354*6ba12bf3SMatthew Dillon 	}
355*6ba12bf3SMatthew Dillon 
356*6ba12bf3SMatthew Dillon 	smbus_release_bus(bus, smbdev);
357*6ba12bf3SMatthew Dillon 
358*6ba12bf3SMatthew Dillon 	return (error);
359*6ba12bf3SMatthew Dillon }
360*6ba12bf3SMatthew Dillon 
361*6ba12bf3SMatthew Dillon DRIVER_MODULE(smb, smbus, smb_driver, smb_devclass, NULL, NULL);
362*6ba12bf3SMatthew Dillon MODULE_DEPEND(smb, smbus, SMBUS_MINVER, SMBUS_PREFVER, SMBUS_MAXVER);
363*6ba12bf3SMatthew Dillon MODULE_VERSION(smb, 1);
364