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