1/*- 2 * Copyright (c) 1998, 2001 Nicolas Souchu 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 * 26 * $FreeBSD$ 27 */ 28 29#ifdef HAVE_KERNEL_OPTION_HEADERS 30#include "opt_compat.h" 31#endif 32 33#include <sys/param.h> 34#include <sys/kernel.h> 35#include <sys/systm.h> 36#include <sys/module.h> 37#include <sys/bus.h> 38#include <sys/conf.h> 39#include <sys/uio.h> 40#include <sys/fcntl.h> 41 42#include <dev/smbus/smbconf.h> 43#include <dev/smbus/smbus.h> 44#include <dev/smbus/smb.h> 45 46#include "smbus_if.h" 47 48#define BUFSIZE 1024 49 50struct smb_softc { 51 device_t sc_dev; 52 int sc_count; /* >0 if device opened */ 53 struct cdev *sc_devnode; 54 struct mtx sc_lock; 55}; 56 57static void smb_identify(driver_t *driver, device_t parent); 58static int smb_probe(device_t); 59static int smb_attach(device_t); 60static int smb_detach(device_t); 61 62static devclass_t smb_devclass; 63 64static device_method_t smb_methods[] = { 65 /* device interface */ 66 DEVMETHOD(device_identify, smb_identify), 67 DEVMETHOD(device_probe, smb_probe), 68 DEVMETHOD(device_attach, smb_attach), 69 DEVMETHOD(device_detach, smb_detach), 70 71 /* smbus interface */ 72 DEVMETHOD(smbus_intr, smbus_generic_intr), 73 74 { 0, 0 } 75}; 76 77static driver_t smb_driver = { 78 "smb", 79 smb_methods, 80 sizeof(struct smb_softc), 81}; 82 83static d_open_t smbopen; 84static d_close_t smbclose; 85static d_ioctl_t smbioctl; 86 87static struct cdevsw smb_cdevsw = { 88 .d_version = D_VERSION, 89 .d_flags = D_TRACKCLOSE, 90 .d_open = smbopen, 91 .d_close = smbclose, 92 .d_ioctl = smbioctl, 93 .d_name = "smb", 94}; 95 96static void 97smb_identify(driver_t *driver, device_t parent) 98{ 99 100 if (device_find_child(parent, "smb", -1) == NULL) 101 BUS_ADD_CHILD(parent, 0, "smb", -1); 102} 103 104static int 105smb_probe(device_t dev) 106{ 107 device_set_desc(dev, "SMBus generic I/O"); 108 109 return (0); 110} 111 112static int 113smb_attach(device_t dev) 114{ 115 struct smb_softc *sc = device_get_softc(dev); 116 117 sc->sc_dev = dev; 118 sc->sc_devnode = make_dev(&smb_cdevsw, device_get_unit(dev), 119 UID_ROOT, GID_WHEEL, 0600, "smb%d", device_get_unit(dev)); 120 sc->sc_devnode->si_drv1 = sc; 121 mtx_init(&sc->sc_lock, device_get_nameunit(dev), NULL, MTX_DEF); 122 123 return (0); 124} 125 126static int 127smb_detach(device_t dev) 128{ 129 struct smb_softc *sc = (struct smb_softc *)device_get_softc(dev); 130 131 if (sc->sc_devnode) 132 destroy_dev(sc->sc_devnode); 133 mtx_destroy(&sc->sc_lock); 134 135 return (0); 136} 137 138static int 139smbopen(struct cdev *dev, int flags, int fmt, struct thread *td) 140{ 141 struct smb_softc *sc = dev->si_drv1; 142 143 mtx_lock(&sc->sc_lock); 144 if (sc->sc_count != 0) { 145 mtx_unlock(&sc->sc_lock); 146 return (EBUSY); 147 } 148 149 sc->sc_count++; 150 mtx_unlock(&sc->sc_lock); 151 152 return (0); 153} 154 155static int 156smbclose(struct cdev *dev, int flags, int fmt, struct thread *td) 157{ 158 struct smb_softc *sc = dev->si_drv1; 159 160 mtx_lock(&sc->sc_lock); 161 KASSERT(sc->sc_count == 1, ("device not busy")); 162 sc->sc_count--; 163 mtx_unlock(&sc->sc_lock); 164 165 return (0); 166} 167 168static int 169smbioctl(struct cdev *dev, u_long cmd, caddr_t data, int flags, struct thread *td) 170{ 171 char buf[SMB_MAXBLOCKSIZE]; 172 device_t parent; 173 struct smbcmd *s = (struct smbcmd *)data; 174 struct smb_softc *sc = dev->si_drv1; 175 device_t smbdev = sc->sc_dev; 176 int error; 177 short w; 178 u_char count; 179 char c; 180 181 parent = device_get_parent(smbdev); 182 183 /* Make sure that LSB bit is cleared. */ 184 if (s->slave & 0x1) 185 return (EINVAL); 186 187 /* Allocate the bus. */ 188 if ((error = smbus_request_bus(parent, smbdev, 189 (flags & O_NONBLOCK) ? SMB_DONTWAIT : (SMB_WAIT | SMB_INTR)))) 190 return (error); 191 192 switch (cmd) { 193 case SMB_QUICK_WRITE: 194 error = smbus_error(smbus_quick(parent, s->slave, SMB_QWRITE)); 195 break; 196 197 case SMB_QUICK_READ: 198 error = smbus_error(smbus_quick(parent, s->slave, SMB_QREAD)); 199 break; 200 201 case SMB_SENDB: 202 error = smbus_error(smbus_sendb(parent, s->slave, s->cmd)); 203 break; 204 205 case SMB_RECVB: 206 error = smbus_error(smbus_recvb(parent, s->slave, &s->cmd)); 207 break; 208 209 case SMB_WRITEB: 210 error = smbus_error(smbus_writeb(parent, s->slave, s->cmd, 211 s->data.byte)); 212 break; 213 214 case SMB_WRITEW: 215 error = smbus_error(smbus_writew(parent, s->slave, 216 s->cmd, s->data.word)); 217 break; 218 219 case SMB_READB: 220 if (s->data.byte_ptr) { 221 error = smbus_error(smbus_readb(parent, s->slave, 222 s->cmd, &c)); 223 if (error) 224 break; 225 error = copyout(&c, s->data.byte_ptr, 226 sizeof(*(s->data.byte_ptr))); 227 } 228 break; 229 230 case SMB_READW: 231 if (s->data.word_ptr) { 232 error = smbus_error(smbus_readw(parent, s->slave, 233 s->cmd, &w)); 234 if (error == 0) { 235 error = copyout(&w, s->data.word_ptr, 236 sizeof(*(s->data.word_ptr))); 237 } 238 } 239 break; 240 241 case SMB_PCALL: 242 if (s->data.process.rdata) { 243 244 error = smbus_error(smbus_pcall(parent, s->slave, s->cmd, 245 s->data.process.sdata, &w)); 246 if (error) 247 break; 248 error = copyout(&w, s->data.process.rdata, 249 sizeof(*(s->data.process.rdata))); 250 } 251 252 break; 253 254 case SMB_BWRITE: 255 if (s->count && s->data.byte_ptr) { 256 if (s->count > SMB_MAXBLOCKSIZE) 257 s->count = SMB_MAXBLOCKSIZE; 258 error = copyin(s->data.byte_ptr, buf, s->count); 259 if (error) 260 break; 261 error = smbus_error(smbus_bwrite(parent, s->slave, 262 s->cmd, s->count, buf)); 263 } 264 break; 265 266#if defined(COMPAT_FREEBSD4) || defined(COMPAT_FREEBSD5) || defined(COMPAT_FREEBSD6) 267 case SMB_OLD_BREAD: 268#endif 269 case SMB_BREAD: 270 if (s->count && s->data.byte_ptr) { 271 count = min(s->count, SMB_MAXBLOCKSIZE); 272 error = smbus_error(smbus_bread(parent, s->slave, 273 s->cmd, &count, buf)); 274 if (error) 275 break; 276 error = copyout(buf, s->data.byte_ptr, 277 min(count, s->count)); 278 s->count = count; 279 } 280 break; 281 282 default: 283 error = ENOTTY; 284 } 285 286 smbus_release_bus(parent, smbdev); 287 288 return (error); 289} 290 291DRIVER_MODULE(smb, smbus, smb_driver, smb_devclass, 0, 0); 292MODULE_DEPEND(smb, smbus, SMBUS_MINVER, SMBUS_PREFVER, SMBUS_MAXVER); 293MODULE_VERSION(smb, 1); 294