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 #include <sys/param.h> 30 #include <sys/kernel.h> 31 #include <sys/systm.h> 32 #include <sys/module.h> 33 #include <sys/bus.h> 34 #include <sys/conf.h> 35 #include <sys/uio.h> 36 #include <sys/fcntl.h> 37 38 #include <dev/smbus/smbconf.h> 39 #include <dev/smbus/smbus.h> 40 #include <dev/smbus/smb.h> 41 42 #include "smbus_if.h" 43 44 #define BUFSIZE 1024 45 46 struct smb_softc { 47 48 int sc_count; /* >0 if device opened */ 49 struct cdev *sc_devnode; 50 }; 51 52 #define IIC_SOFTC(unit) \ 53 ((struct smb_softc *)devclass_get_softc(smb_devclass, (unit))) 54 55 #define IIC_DEVICE(unit) \ 56 (devclass_get_device(smb_devclass, (unit))) 57 58 static int smb_probe(device_t); 59 static int smb_attach(device_t); 60 static int smb_detach(device_t); 61 static void smb_identify(driver_t *driver, device_t parent); 62 63 static devclass_t smb_devclass; 64 65 static device_method_t smb_methods[] = { 66 /* device interface */ 67 DEVMETHOD(device_identify, smb_identify), 68 DEVMETHOD(device_probe, smb_probe), 69 DEVMETHOD(device_attach, smb_attach), 70 DEVMETHOD(device_detach, smb_detach), 71 72 /* smbus interface */ 73 DEVMETHOD(smbus_intr, smbus_generic_intr), 74 75 { 0, 0 } 76 }; 77 78 static driver_t smb_driver = { 79 "smb", 80 smb_methods, 81 sizeof(struct smb_softc), 82 }; 83 84 static d_open_t smbopen; 85 static d_close_t smbclose; 86 static d_ioctl_t smbioctl; 87 88 static struct cdevsw smb_cdevsw = { 89 .d_version = D_VERSION, 90 .d_flags = D_NEEDGIANT, 91 .d_open = smbopen, 92 .d_close = smbclose, 93 .d_ioctl = smbioctl, 94 .d_name = "smb", 95 }; 96 97 static void 98 smb_identify(driver_t *driver, device_t parent) 99 { 100 BUS_ADD_CHILD(parent, 0, "smb", -1); 101 } 102 103 static int 104 smb_probe(device_t dev) 105 { 106 device_set_desc(dev, "SMBus generic I/O"); 107 108 return (0); 109 } 110 111 static int 112 smb_attach(device_t dev) 113 { 114 struct smb_softc *sc = (struct smb_softc *)device_get_softc(dev); 115 116 if (!sc) 117 return (ENOMEM); 118 119 bzero(sc, sizeof(struct smb_softc *)); 120 121 sc->sc_devnode = make_dev(&smb_cdevsw, device_get_unit(dev), 122 UID_ROOT, GID_WHEEL, 123 0600, "smb%d", device_get_unit(dev)); 124 125 return (0); 126 } 127 128 static int 129 smb_detach(device_t dev) 130 { 131 struct smb_softc *sc = (struct smb_softc *)device_get_softc(dev); 132 133 if (sc->sc_devnode) 134 destroy_dev(sc->sc_devnode); 135 136 return (0); 137 } 138 139 static int 140 smbopen (struct cdev *dev, int flags, int fmt, struct thread *td) 141 { 142 struct smb_softc *sc = IIC_SOFTC(minor(dev)); 143 144 if (sc == NULL) 145 return (ENXIO); 146 147 if (sc->sc_count != 0) 148 return (EBUSY); 149 150 sc->sc_count++; 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 = IIC_SOFTC(minor(dev)); 159 160 if (sc == NULL) 161 return (ENXIO); 162 163 if (sc->sc_count == 0) 164 /* This is not supposed to happen. */ 165 return (0); 166 167 sc->sc_count--; 168 169 return (0); 170 } 171 172 static int 173 smbioctl(struct cdev *dev, u_long cmd, caddr_t data, int flags, struct thread *td) 174 { 175 char buf[SMB_MAXBLOCKSIZE]; 176 device_t parent; 177 struct smbcmd *s = (struct smbcmd *)data; 178 struct smb_softc *sc = IIC_SOFTC(minor(dev)); 179 device_t smbdev = IIC_DEVICE(minor(dev)); 180 int error; 181 short w; 182 char c; 183 184 if (sc == NULL) 185 return (ENXIO); 186 if (s == NULL) 187 return (EINVAL); 188 189 parent = device_get_parent(smbdev); 190 191 /* Allocate the bus. */ 192 if ((error = smbus_request_bus(parent, smbdev, 193 (flags & O_NONBLOCK) ? SMB_DONTWAIT : (SMB_WAIT | SMB_INTR)))) 194 return (error); 195 196 switch (cmd) { 197 case SMB_QUICK_WRITE: 198 error = smbus_error(smbus_quick(parent, s->slave, SMB_QWRITE)); 199 break; 200 201 case SMB_QUICK_READ: 202 error = smbus_error(smbus_quick(parent, s->slave, SMB_QREAD)); 203 break; 204 205 case SMB_SENDB: 206 error = smbus_error(smbus_sendb(parent, s->slave, s->cmd)); 207 break; 208 209 case SMB_RECVB: 210 error = smbus_error(smbus_recvb(parent, s->slave, &s->cmd)); 211 break; 212 213 case SMB_WRITEB: 214 error = smbus_error(smbus_writeb(parent, s->slave, s->cmd, 215 s->data.byte)); 216 break; 217 218 case SMB_WRITEW: 219 error = smbus_error(smbus_writew(parent, s->slave, 220 s->cmd, s->data.word)); 221 break; 222 223 case SMB_READB: 224 if (s->data.byte_ptr) { 225 error = smbus_error(smbus_readb(parent, s->slave, 226 s->cmd, &c)); 227 if (error) 228 break; 229 error = copyout(&c, s->data.byte_ptr, 230 sizeof(*(s->data.byte_ptr))); 231 } 232 break; 233 234 case SMB_READW: 235 if (s->data.word_ptr) { 236 error = smbus_error(smbus_readw(parent, s->slave, 237 s->cmd, &w)); 238 if (error == 0) { 239 error = copyout(&w, s->data.word_ptr, 240 sizeof(*(s->data.word_ptr))); 241 } 242 } 243 break; 244 245 case SMB_PCALL: 246 if (s->data.process.rdata) { 247 248 error = smbus_error(smbus_pcall(parent, s->slave, s->cmd, 249 s->data.process.sdata, &w)); 250 if (error) 251 break; 252 error = copyout(&w, s->data.process.rdata, 253 sizeof(*(s->data.process.rdata))); 254 } 255 256 break; 257 258 case SMB_BWRITE: 259 if (s->count && s->data.byte_ptr) { 260 if (s->count > SMB_MAXBLOCKSIZE) 261 s->count = SMB_MAXBLOCKSIZE; 262 error = copyin(s->data.byte_ptr, buf, s->count); 263 if (error) 264 break; 265 error = smbus_error(smbus_bwrite(parent, s->slave, 266 s->cmd, s->count, buf)); 267 } 268 break; 269 270 case SMB_BREAD: 271 if (s->count && s->data.byte_ptr) { 272 if (s->count > SMB_MAXBLOCKSIZE) 273 s->count = SMB_MAXBLOCKSIZE; 274 error = smbus_error(smbus_bread(parent, s->slave, 275 s->cmd, s->count, buf)); 276 if (error) 277 break; 278 error = copyout(buf, s->data.byte_ptr, s->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