138776Snsouch/*-
293023Snsouch * Copyright (c) 1998, 2001 Nicolas Souchu
338776Snsouch * All rights reserved.
438776Snsouch *
538776Snsouch * Redistribution and use in source and binary forms, with or without
638776Snsouch * modification, are permitted provided that the following conditions
738776Snsouch * are met:
838776Snsouch * 1. Redistributions of source code must retain the above copyright
938776Snsouch *    notice, this list of conditions and the following disclaimer.
1038776Snsouch * 2. Redistributions in binary form must reproduce the above copyright
1138776Snsouch *    notice, this list of conditions and the following disclaimer in the
1238776Snsouch *    documentation and/or other materials provided with the distribution.
1338776Snsouch *
1438776Snsouch * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1538776Snsouch * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1638776Snsouch * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1738776Snsouch * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1838776Snsouch * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1938776Snsouch * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2038776Snsouch * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2138776Snsouch * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2238776Snsouch * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2338776Snsouch * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2438776Snsouch * SUCH DAMAGE.
2538776Snsouch *
2650477Speter * $FreeBSD$
2738776Snsouch */
28129352Sjoerg
29162662Sjhb#ifdef HAVE_KERNEL_OPTION_HEADERS
30162662Sjhb#include "opt_compat.h"
31162662Sjhb#endif
32162662Sjhb
3338776Snsouch#include <sys/param.h>
3438776Snsouch#include <sys/kernel.h>
3538776Snsouch#include <sys/systm.h>
3638776Snsouch#include <sys/module.h>
3738776Snsouch#include <sys/bus.h>
3838776Snsouch#include <sys/conf.h>
3938776Snsouch#include <sys/uio.h>
4043975Snsouch#include <sys/fcntl.h>
4138776Snsouch
4238776Snsouch#include <dev/smbus/smbconf.h>
4338776Snsouch#include <dev/smbus/smbus.h>
44103588Speter#include <dev/smbus/smb.h>
4538776Snsouch
4638776Snsouch#include "smbus_if.h"
4738776Snsouch
4838776Snsouch#define BUFSIZE 1024
4938776Snsouch
5038776Snsouchstruct smb_softc {
51179625Sjhb	device_t sc_dev;
5238776Snsouch	int sc_count;			/* >0 if device opened */
53130585Sphk	struct cdev *sc_devnode;
54179625Sjhb	struct mtx sc_lock;
5538776Snsouch};
5638776Snsouch
57162234Sjhbstatic void smb_identify(driver_t *driver, device_t parent);
5838776Snsouchstatic int smb_probe(device_t);
5938776Snsouchstatic int smb_attach(device_t);
6093023Snsouchstatic int smb_detach(device_t);
6138776Snsouch
6238776Snsouchstatic devclass_t smb_devclass;
6338776Snsouch
6438776Snsouchstatic device_method_t smb_methods[] = {
6538776Snsouch	/* device interface */
66162234Sjhb	DEVMETHOD(device_identify,	smb_identify),
6738776Snsouch	DEVMETHOD(device_probe,		smb_probe),
6838776Snsouch	DEVMETHOD(device_attach,	smb_attach),
6993023Snsouch	DEVMETHOD(device_detach,	smb_detach),
7038776Snsouch
7138776Snsouch	/* smbus interface */
7238776Snsouch	DEVMETHOD(smbus_intr,		smbus_generic_intr),
7338776Snsouch
7438776Snsouch	{ 0, 0 }
7538776Snsouch};
7638776Snsouch
7738776Snsouchstatic driver_t smb_driver = {
7838776Snsouch	"smb",
7938776Snsouch	smb_methods,
8038776Snsouch	sizeof(struct smb_softc),
8138776Snsouch};
8238776Snsouch
8338776Snsouchstatic	d_open_t	smbopen;
8438776Snsouchstatic	d_close_t	smbclose;
8538776Snsouchstatic	d_ioctl_t	smbioctl;
8638776Snsouch
8747625Sphkstatic struct cdevsw smb_cdevsw = {
88126080Sphk	.d_version =	D_VERSION,
89179625Sjhb	.d_flags =	D_TRACKCLOSE,
90111815Sphk	.d_open =	smbopen,
91111815Sphk	.d_close =	smbclose,
92111815Sphk	.d_ioctl =	smbioctl,
93111815Sphk	.d_name =	"smb",
9447625Sphk};
9538776Snsouch
96162234Sjhbstatic void
97162234Sjhbsmb_identify(driver_t *driver, device_t parent)
98162234Sjhb{
99162234Sjhb
100162234Sjhb	if (device_find_child(parent, "smb", -1) == NULL)
101162234Sjhb		BUS_ADD_CHILD(parent, 0, "smb", -1);
102162234Sjhb}
103162234Sjhb
10438776Snsouchstatic int
10538776Snsouchsmb_probe(device_t dev)
10638776Snsouch{
10793023Snsouch	device_set_desc(dev, "SMBus generic I/O");
10838776Snsouch
10938776Snsouch	return (0);
11038776Snsouch}
11138776Snsouch
11238776Snsouchstatic int
11338776Snsouchsmb_attach(device_t dev)
11438776Snsouch{
115179625Sjhb	struct smb_softc *sc = device_get_softc(dev);
11693023Snsouch
117179625Sjhb	sc->sc_dev = dev;
11893023Snsouch	sc->sc_devnode = make_dev(&smb_cdevsw, device_get_unit(dev),
119179625Sjhb	    UID_ROOT, GID_WHEEL, 0600, "smb%d", device_get_unit(dev));
120179625Sjhb	sc->sc_devnode->si_drv1 = sc;
121179625Sjhb	mtx_init(&sc->sc_lock, device_get_nameunit(dev), NULL, MTX_DEF);
12293023Snsouch
12338776Snsouch	return (0);
12438776Snsouch}
12538776Snsouch
12638776Snsouchstatic int
12793023Snsouchsmb_detach(device_t dev)
12893023Snsouch{
12993023Snsouch	struct smb_softc *sc = (struct smb_softc *)device_get_softc(dev);
13093023Snsouch
13193023Snsouch	if (sc->sc_devnode)
13293023Snsouch		destroy_dev(sc->sc_devnode);
133179625Sjhb	mtx_destroy(&sc->sc_lock);
13493023Snsouch
13593023Snsouch	return (0);
13693023Snsouch}
13793023Snsouch
13893023Snsouchstatic int
139179625Sjhbsmbopen(struct cdev *dev, int flags, int fmt, struct thread *td)
14038776Snsouch{
141179625Sjhb	struct smb_softc *sc = dev->si_drv1;
14238776Snsouch
143179625Sjhb	mtx_lock(&sc->sc_lock);
144179625Sjhb	if (sc->sc_count != 0) {
145179625Sjhb		mtx_unlock(&sc->sc_lock);
14638776Snsouch		return (EBUSY);
147179625Sjhb	}
14838776Snsouch
14938776Snsouch	sc->sc_count++;
150179625Sjhb	mtx_unlock(&sc->sc_lock);
15138776Snsouch
15238776Snsouch	return (0);
15338776Snsouch}
15438776Snsouch
15538776Snsouchstatic int
156130585Sphksmbclose(struct cdev *dev, int flags, int fmt, struct thread *td)
15738776Snsouch{
158179625Sjhb	struct smb_softc *sc = dev->si_drv1;
15938776Snsouch
160179625Sjhb	mtx_lock(&sc->sc_lock);
161179625Sjhb	KASSERT(sc->sc_count == 1, ("device not busy"));
16238776Snsouch	sc->sc_count--;
163179625Sjhb	mtx_unlock(&sc->sc_lock);
16438776Snsouch
16538776Snsouch	return (0);
16638776Snsouch}
16738776Snsouch
16838776Snsouchstatic int
169130585Sphksmbioctl(struct cdev *dev, u_long cmd, caddr_t data, int flags, struct thread *td)
17038776Snsouch{
171129352Sjoerg	char buf[SMB_MAXBLOCKSIZE];
172129352Sjoerg	device_t parent;
173129352Sjoerg	struct smbcmd *s = (struct smbcmd *)data;
174179625Sjhb	struct smb_softc *sc = dev->si_drv1;
175179625Sjhb	device_t smbdev = sc->sc_dev;
176129352Sjoerg	int error;
177129352Sjoerg	short w;
178162234Sjhb	u_char count;
179129290Sjoerg	char c;
18038776Snsouch
181129352Sjoerg	parent = device_get_parent(smbdev);
182129352Sjoerg
183188077Sjhb	/* Make sure that LSB bit is cleared. */
184188077Sjhb	if (s->slave & 0x1)
185188077Sjhb		return (EINVAL);
186188077Sjhb
187129352Sjoerg	/* Allocate the bus. */
18843975Snsouch	if ((error = smbus_request_bus(parent, smbdev,
18943975Snsouch			(flags & O_NONBLOCK) ? SMB_DONTWAIT : (SMB_WAIT | SMB_INTR))))
19043975Snsouch		return (error);
19143975Snsouch
19238776Snsouch	switch (cmd) {
19338776Snsouch	case SMB_QUICK_WRITE:
19443998Snsouch		error = smbus_error(smbus_quick(parent, s->slave, SMB_QWRITE));
19543975Snsouch		break;
19638776Snsouch
19738776Snsouch	case SMB_QUICK_READ:
19843998Snsouch		error = smbus_error(smbus_quick(parent, s->slave, SMB_QREAD));
19943975Snsouch		break;
20038776Snsouch
20138776Snsouch	case SMB_SENDB:
20243998Snsouch		error = smbus_error(smbus_sendb(parent, s->slave, s->cmd));
20338776Snsouch		break;
20438776Snsouch
20538776Snsouch	case SMB_RECVB:
20643998Snsouch		error = smbus_error(smbus_recvb(parent, s->slave, &s->cmd));
20738776Snsouch		break;
20838776Snsouch
20938776Snsouch	case SMB_WRITEB:
21043998Snsouch		error = smbus_error(smbus_writeb(parent, s->slave, s->cmd,
21143998Snsouch						s->data.byte));
21238776Snsouch		break;
21338776Snsouch
21438776Snsouch	case SMB_WRITEW:
21543998Snsouch		error = smbus_error(smbus_writew(parent, s->slave,
21643998Snsouch						s->cmd, s->data.word));
21738776Snsouch		break;
21838776Snsouch
21938776Snsouch	case SMB_READB:
220129290Sjoerg		if (s->data.byte_ptr) {
22143998Snsouch			error = smbus_error(smbus_readb(parent, s->slave,
222129290Sjoerg						s->cmd, &c));
223129290Sjoerg			if (error)
224129290Sjoerg				break;
225129290Sjoerg			error = copyout(&c, s->data.byte_ptr,
226129290Sjoerg					sizeof(*(s->data.byte_ptr)));
227129290Sjoerg		}
22838776Snsouch		break;
22938776Snsouch
23038776Snsouch	case SMB_READW:
231129290Sjoerg		if (s->data.word_ptr) {
23243998Snsouch			error = smbus_error(smbus_readw(parent, s->slave,
233129290Sjoerg						s->cmd, &w));
234129290Sjoerg			if (error == 0) {
235129290Sjoerg				error = copyout(&w, s->data.word_ptr,
236129290Sjoerg						sizeof(*(s->data.word_ptr)));
237129290Sjoerg			}
238129290Sjoerg		}
23938776Snsouch		break;
24038776Snsouch
24138776Snsouch	case SMB_PCALL:
242129290Sjoerg		if (s->data.process.rdata) {
243129290Sjoerg
24443998Snsouch			error = smbus_error(smbus_pcall(parent, s->slave, s->cmd,
245129290Sjoerg				s->data.process.sdata, &w));
246129290Sjoerg			if (error)
247129290Sjoerg				break;
248129290Sjoerg			error = copyout(&w, s->data.process.rdata,
249129290Sjoerg					sizeof(*(s->data.process.rdata)));
250129290Sjoerg		}
251129290Sjoerg
25238776Snsouch		break;
25338776Snsouch
25438776Snsouch	case SMB_BWRITE:
255129290Sjoerg		if (s->count && s->data.byte_ptr) {
256129290Sjoerg			if (s->count > SMB_MAXBLOCKSIZE)
257129290Sjoerg				s->count = SMB_MAXBLOCKSIZE;
258129290Sjoerg			error = copyin(s->data.byte_ptr, buf, s->count);
259129290Sjoerg			if (error)
260129290Sjoerg				break;
26143998Snsouch			error = smbus_error(smbus_bwrite(parent, s->slave,
262129290Sjoerg						s->cmd, s->count, buf));
263129290Sjoerg		}
26438776Snsouch		break;
26538776Snsouch
266162662Sjhb#if defined(COMPAT_FREEBSD4) || defined(COMPAT_FREEBSD5) || defined(COMPAT_FREEBSD6)
267162234Sjhb	case SMB_OLD_BREAD:
268162662Sjhb#endif
26938776Snsouch	case SMB_BREAD:
270129290Sjoerg		if (s->count && s->data.byte_ptr) {
271162234Sjhb			count = min(s->count, SMB_MAXBLOCKSIZE);
27243998Snsouch			error = smbus_error(smbus_bread(parent, s->slave,
273162234Sjhb						s->cmd, &count, buf));
274129290Sjoerg			if (error)
275129290Sjoerg				break;
276162234Sjhb			error = copyout(buf, s->data.byte_ptr,
277162234Sjhb			    min(count, s->count));
278162234Sjhb			s->count = count;
279129290Sjoerg		}
28038776Snsouch		break;
28138776Snsouch
28238776Snsouch	default:
283129290Sjoerg		error = ENOTTY;
28438776Snsouch	}
28538776Snsouch
28643975Snsouch	smbus_release_bus(parent, smbdev);
28743975Snsouch
28838776Snsouch	return (error);
28938776Snsouch}
29038776Snsouch
29152999SpeterDRIVER_MODULE(smb, smbus, smb_driver, smb_devclass, 0, 0);
29293023SnsouchMODULE_DEPEND(smb, smbus, SMBUS_MINVER, SMBUS_PREFVER, SMBUS_MAXVER);
29393023SnsouchMODULE_VERSION(smb, 1);
294