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 50 struct 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 57 static void smb_identify(driver_t *driver, device_t parent); 58 static int smb_probe(device_t); 59 static int smb_attach(device_t); 60 static int smb_detach(device_t); 61 62 static devclass_t smb_devclass; 63 64 static 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 77 static driver_t smb_driver = { 78 "smb", 79 smb_methods, 80 sizeof(struct smb_softc), 81 }; 82 83 static d_open_t smbopen; 84 static d_close_t smbclose; 85 static d_ioctl_t smbioctl; 86 87 static 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 96 static void 97 smb_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 104 static int 105 smb_probe(device_t dev) 106 { 107 device_set_desc(dev, "SMBus generic I/O"); 108 109 return (0); 110 } 111 112 static int 113 smb_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 126 static int 127 smb_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 138 static int 139 smbopen(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 155 static int 156 smbclose(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 168 static int 169 smbioctl(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 291 DRIVER_MODULE(smb, smbus, smb_driver, smb_devclass, 0, 0); 292 MODULE_DEPEND(smb, smbus, SMBUS_MINVER, SMBUS_PREFVER, SMBUS_MAXVER); 293 MODULE_VERSION(smb, 1); 294