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: src/sys/dev/smbus/smb.c,v 1.34.8.2 2006/09/22 19:19:16 jhb Exp $ 27 * 28 */ 29 30 #include <sys/param.h> 31 #include <sys/kernel.h> 32 #include <sys/systm.h> 33 #include <sys/device.h> 34 #include <sys/module.h> 35 #include <sys/bus.h> 36 #include <sys/conf.h> 37 #include <sys/uio.h> 38 #include <sys/fcntl.h> 39 40 #include <bus/smbus/smbconf.h> 41 #include <bus/smbus/smbus.h> 42 #include "smb.h" 43 44 #include "smbus_if.h" 45 46 #define BUFSIZE 1024 47 48 struct smb_softc { 49 device_t sc_dev; 50 int sc_count; /* >0 if device opened */ 51 int sc_unit; 52 cdev_t sc_devnode; 53 }; 54 55 #define SMB_SOFTC(unit) \ 56 ((struct smb_softc *)devclass_get_softc(smb_devclass, (unit))) 57 58 #define SMB_DEVICE(unit) \ 59 (devclass_get_device(smb_devclass, (unit))) 60 61 static void smb_identify(driver_t *driver, device_t parent); 62 static int smb_probe(device_t); 63 static int smb_attach(device_t); 64 static int smb_detach(device_t); 65 66 static devclass_t smb_devclass; 67 68 static device_method_t smb_methods[] = { 69 /* device interface */ 70 DEVMETHOD(device_identify, smb_identify), 71 DEVMETHOD(device_probe, smb_probe), 72 DEVMETHOD(device_attach, smb_attach), 73 DEVMETHOD(device_detach, smb_detach), 74 75 #if 0 76 /* bus interface */ 77 DEVMETHOD(bus_driver_added, bus_generic_driver_added), 78 DEVMETHOD(bus_print_child, bus_generic_print_child), 79 #endif 80 81 /* smbus interface */ 82 DEVMETHOD(smbus_intr, smbus_generic_intr), 83 84 DEVMETHOD_END 85 }; 86 87 static driver_t smb_driver = { 88 "smb", 89 smb_methods, 90 sizeof(struct smb_softc), 91 }; 92 93 static d_open_t smbopen; 94 static d_close_t smbclose; 95 static d_ioctl_t smbioctl; 96 97 static struct dev_ops smb_ops = { 98 { "smb", 0, 0 }, 99 .d_open = smbopen, 100 .d_close = smbclose, 101 .d_ioctl = smbioctl, 102 }; 103 104 static void 105 smb_identify(driver_t *driver, device_t parent) 106 { 107 if (device_find_child(parent, "smb", -1) == NULL) 108 BUS_ADD_CHILD(parent, parent, 0, "smb", -1); 109 } 110 111 static int 112 smb_probe(device_t dev) 113 { 114 device_set_desc(dev, "SMBus generic I/O"); 115 116 /* Allow other subclasses to override this driver. */ 117 return (BUS_PROBE_GENERIC); 118 } 119 120 static int 121 smb_attach(device_t dev) 122 { 123 struct smb_softc *sc = (struct smb_softc *)device_get_softc(dev); 124 int unit; 125 126 if (!sc) 127 return (ENOMEM); 128 129 bzero(sc, sizeof(struct smb_softc *)); 130 unit = device_get_unit(dev); 131 sc->sc_dev = dev; 132 sc->sc_unit = unit; 133 134 if (unit & 0x0400) { 135 sc->sc_devnode = make_dev(&smb_ops, unit, 136 UID_ROOT, GID_WHEEL, 0600, 137 "smb%d-%02x", unit >> 11, unit & 1023); 138 } else { 139 sc->sc_devnode = make_dev(&smb_ops, unit, 140 UID_ROOT, GID_WHEEL, 0600, "smb%d", unit); 141 } 142 143 return (0); 144 } 145 146 static int 147 smb_detach(device_t dev) 148 { 149 struct smb_softc *sc = (struct smb_softc *)device_get_softc(dev); 150 151 if (sc->sc_devnode) 152 dev_ops_remove_minor(&smb_ops, device_get_unit(dev)); 153 154 return (0); 155 } 156 157 static int 158 smbopen (struct dev_open_args *ap) 159 { 160 cdev_t dev = ap->a_head.a_dev; 161 struct smb_softc *sc = SMB_SOFTC(minor(dev)); 162 163 if (sc == NULL) 164 return (ENXIO); 165 166 if (sc->sc_count != 0) 167 return (EBUSY); 168 169 sc->sc_count++; 170 171 return (0); 172 } 173 174 static int 175 smbclose(struct dev_close_args *ap) 176 { 177 cdev_t dev = ap->a_head.a_dev; 178 struct smb_softc *sc = SMB_SOFTC(minor(dev)); 179 180 if (sc == NULL) 181 return (ENXIO); 182 183 if (sc->sc_count == 0) 184 /* This is not supposed to happen. */ 185 return (0); 186 187 sc->sc_count--; 188 189 return (0); 190 } 191 192 #if 0 193 static int 194 smbwrite(struct dev_write_args *ap) 195 { 196 return (EINVAL); 197 } 198 199 static int 200 smbread(struct dev_read_args *ap) 201 { 202 return (EINVAL); 203 } 204 #endif 205 206 static int 207 smbioctl(struct dev_ioctl_args *ap) 208 { 209 cdev_t dev = ap->a_head.a_dev; 210 device_t bus; /* smbbus */ 211 char buf[SMB_MAXBLOCKSIZE]; 212 struct smbcmd *s = (struct smbcmd *)ap->a_data; 213 struct smb_softc *sc = SMB_SOFTC(minor(dev)); 214 device_t smbdev = SMB_DEVICE(minor(dev)); 215 int error; 216 int unit; 217 u_char bcount; 218 219 if (sc == NULL) 220 return (ENXIO); 221 if (s == NULL) 222 return (EINVAL); 223 224 /* 225 * If a specific slave device is being used, override any passed-in 226 * slave. 227 */ 228 unit = sc->sc_unit; 229 if (unit & 0x0400) { 230 s->slave = unit & 1023; 231 } 232 233 /* 234 * NOTE: smbus_*() functions automatically recurse the parent to 235 * get to the actual device driver. 236 */ 237 bus = device_get_parent(smbdev); /* smbus */ 238 239 /* Allocate the bus. */ 240 if ((error = smbus_request_bus(bus, smbdev, 241 (ap->a_fflag & O_NONBLOCK) ? 242 SMB_DONTWAIT : (SMB_WAIT | SMB_INTR)))) 243 return (error); 244 245 switch (ap->a_cmd) { 246 case SMB_QUICK_WRITE: 247 error = smbus_error(smbus_quick(bus, s->slave, SMB_QWRITE)); 248 break; 249 250 case SMB_QUICK_READ: 251 error = smbus_error(smbus_quick(bus, s->slave, SMB_QREAD)); 252 break; 253 254 case SMB_SENDB: 255 error = smbus_error(smbus_sendb(bus, s->slave, s->cmd)); 256 break; 257 258 case SMB_RECVB: 259 error = smbus_error(smbus_recvb(bus, s->slave, &s->cmd)); 260 break; 261 262 case SMB_WRITEB: 263 error = smbus_error(smbus_writeb(bus, s->slave, s->cmd, 264 s->wdata.byte)); 265 break; 266 267 case SMB_WRITEW: 268 error = smbus_error(smbus_writew(bus, s->slave, s->cmd, 269 s->wdata.word)); 270 break; 271 272 case SMB_READB: 273 error = smbus_error(smbus_readb(bus, s->slave, s->cmd, 274 &s->rdata.byte)); 275 if (s->rbuf && s->rcount >= 1) { 276 error = copyout(&s->rdata.byte, s->rbuf, 1); 277 s->rcount = 1; 278 } 279 break; 280 281 case SMB_READW: 282 error = smbus_error(smbus_readw(bus, s->slave, s->cmd, 283 &s->rdata.word)); 284 if (s->rbuf && s->rcount >= 2) { 285 buf[0] = (u_char)s->rdata.word; 286 buf[1] = (u_char)(s->rdata.word >> 8); 287 error = copyout(buf, s->rbuf, 2); 288 s->rcount = 2; 289 } 290 break; 291 292 case SMB_PCALL: 293 error = smbus_error(smbus_pcall(bus, s->slave, s->cmd, 294 s->wdata.word, &s->rdata.word)); 295 if (s->rbuf && s->rcount >= 2) { 296 char buf[2]; 297 buf[0] = (u_char)s->rdata.word; 298 buf[1] = (u_char)(s->rdata.word >> 8); 299 error = copyout(buf, s->rbuf, 2); 300 s->rcount = 2; 301 } 302 303 break; 304 305 case SMB_BWRITE: 306 if (s->wcount < 0) 307 s->wcount = 0; 308 if (s->wcount > SMB_MAXBLOCKSIZE) 309 s->wcount = SMB_MAXBLOCKSIZE; 310 if (s->wcount) 311 error = copyin(s->wbuf, buf, s->wcount); 312 if (error) 313 break; 314 error = smbus_error(smbus_bwrite(bus, s->slave, s->cmd, 315 s->wcount, buf)); 316 break; 317 318 case SMB_BREAD: 319 if (s->rcount < 0) 320 s->rcount = 0; 321 if (s->rcount > SMB_MAXBLOCKSIZE) 322 s->rcount = SMB_MAXBLOCKSIZE; 323 error = smbus_bread(bus, s->slave, s->cmd, &bcount, buf); 324 error = smbus_error(error); 325 if (error) 326 break; 327 if (s->rcount > bcount) 328 s->rcount = bcount; 329 error = copyout(buf, s->rbuf, s->rcount); 330 break; 331 332 case SMB_TRANS: 333 if (s->rcount < 0) 334 s->rcount = 0; 335 if (s->rcount > SMB_MAXBLOCKSIZE) 336 s->rcount = SMB_MAXBLOCKSIZE; 337 if (s->wcount < 0) 338 s->wcount = 0; 339 if (s->wcount > SMB_MAXBLOCKSIZE) 340 s->wcount = SMB_MAXBLOCKSIZE; 341 if (s->wcount) 342 error = copyin(s->wbuf, buf, s->wcount); 343 if (error) 344 break; 345 error = smbus_trans(bus, s->slave, s->cmd, s->op, 346 buf, s->wcount, buf, s->rcount, &s->rcount); 347 error = smbus_error(error); 348 if (error == 0) 349 error = copyout(buf, s->rbuf, s->rcount); 350 break; 351 default: 352 error = ENOTTY; 353 break; 354 } 355 356 smbus_release_bus(bus, smbdev); 357 358 return (error); 359 } 360 361 DRIVER_MODULE(smb, smbus, smb_driver, smb_devclass, NULL, NULL); 362 MODULE_DEPEND(smb, smbus, SMBUS_MINVER, SMBUS_PREFVER, SMBUS_MAXVER); 363 MODULE_VERSION(smb, 1); 364