if_ic.c revision 186833
138774Snsouch/*- 293023Snsouch * Copyright (c) 1998, 2001 Nicolas Souchu 338774Snsouch * All rights reserved. 438774Snsouch * 538774Snsouch * Redistribution and use in source and binary forms, with or without 638774Snsouch * modification, are permitted provided that the following conditions 738774Snsouch * are met: 838774Snsouch * 1. Redistributions of source code must retain the above copyright 938774Snsouch * notice, this list of conditions and the following disclaimer. 1038774Snsouch * 2. Redistributions in binary form must reproduce the above copyright 1138774Snsouch * notice, this list of conditions and the following disclaimer in the 1238774Snsouch * documentation and/or other materials provided with the distribution. 1338774Snsouch * 1438774Snsouch * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1538774Snsouch * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1638774Snsouch * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1738774Snsouch * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1838774Snsouch * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1938774Snsouch * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2038774Snsouch * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2138774Snsouch * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2238774Snsouch * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2338774Snsouch * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2438774Snsouch * SUCH DAMAGE. 2538774Snsouch */ 2638774Snsouch 27119418Sobrien#include <sys/cdefs.h> 28119418Sobrien__FBSDID("$FreeBSD: head/sys/dev/iicbus/if_ic.c 186833 2009-01-06 17:23:37Z nwhitehorn $"); 29119418Sobrien 3038774Snsouch/* 3138774Snsouch * I2C bus IP driver 3238774Snsouch */ 3338774Snsouch 3438774Snsouch#include <sys/param.h> 3538774Snsouch#include <sys/systm.h> 3638774Snsouch#include <sys/mbuf.h> 3738774Snsouch#include <sys/socket.h> 3838774Snsouch#include <sys/filio.h> 3938774Snsouch#include <sys/sockio.h> 4038774Snsouch#include <sys/kernel.h> 41181305Sjhb#include <sys/lock.h> 4238774Snsouch#include <sys/module.h> 43181305Sjhb#include <sys/mutex.h> 4438774Snsouch#include <sys/bus.h> 4538774Snsouch#include <sys/time.h> 4638774Snsouch#include <sys/malloc.h> 4738774Snsouch 4838774Snsouch#include <net/if.h> 4938774Snsouch#include <net/if_types.h> 5038774Snsouch#include <net/netisr.h> 5138774Snsouch 5238774Snsouch#include <sys/mbuf.h> 5338774Snsouch#include <sys/socket.h> 5438774Snsouch#include <net/netisr.h> 5538774Snsouch#include <net/route.h> 5638774Snsouch#include <netinet/in.h> 5738774Snsouch#include <netinet/in_systm.h> 5838774Snsouch#include <netinet/in_var.h> 5938774Snsouch#include <netinet/ip.h> 6038774Snsouch#include <netinet/if_ether.h> 6138774Snsouch 6238774Snsouch#include <net/bpf.h> 6338774Snsouch 6438774Snsouch#include <dev/iicbus/iiconf.h> 6538774Snsouch#include <dev/iicbus/iicbus.h> 6638774Snsouch 6738774Snsouch#include "iicbus_if.h" 6838774Snsouch 6993023Snsouch#define PCF_MASTER_ADDRESS 0xaa 7093023Snsouch 71123922Ssam#define ICHDRLEN sizeof(u_int32_t) 7238774Snsouch#define ICMTU 1500 /* default mtu */ 7338774Snsouch 7438774Snsouchstruct ic_softc { 75147256Sbrooks struct ifnet *ic_ifp; 76181305Sjhb device_t ic_dev; 7738774Snsouch 7838774Snsouch u_char ic_addr; /* peer I2C address */ 7938774Snsouch 80181305Sjhb int ic_flags; 8138774Snsouch 8238774Snsouch char *ic_obuf; 8338774Snsouch char *ic_ifbuf; 8438774Snsouch char *ic_cp; 8538774Snsouch 8638774Snsouch int ic_xfercnt; 8738774Snsouch 8838774Snsouch int ic_iferrs; 89181305Sjhb 90181305Sjhb struct mtx ic_lock; 9138774Snsouch}; 9238774Snsouch 93181305Sjhb#define IC_SENDING 0x0001 94181305Sjhb#define IC_OBUF_BUSY 0x0002 95181305Sjhb#define IC_IFBUF_BUSY 0x0004 96181305Sjhb#define IC_BUFFERS_BUSY (IC_OBUF_BUSY | IC_IFBUF_BUSY) 97181305Sjhb#define IC_BUFFER_WAITER 0x0004 98181305Sjhb 9938774Snsouchstatic devclass_t ic_devclass; 10038774Snsouch 10138774Snsouchstatic int icprobe(device_t); 10238774Snsouchstatic int icattach(device_t); 10338774Snsouch 10438774Snsouchstatic int icioctl(struct ifnet *, u_long, caddr_t); 10538774Snsouchstatic int icoutput(struct ifnet *, struct mbuf *, struct sockaddr *, 10638774Snsouch struct rtentry *); 10738774Snsouch 10838774Snsouchstatic void icintr(device_t, int, char *); 10938774Snsouch 11038774Snsouchstatic device_method_t ic_methods[] = { 11138774Snsouch /* device interface */ 11238774Snsouch DEVMETHOD(device_probe, icprobe), 11338774Snsouch DEVMETHOD(device_attach, icattach), 11438774Snsouch 11538774Snsouch /* iicbus interface */ 11638774Snsouch DEVMETHOD(iicbus_intr, icintr), 11738774Snsouch 11838774Snsouch { 0, 0 } 11938774Snsouch}; 12038774Snsouch 12138774Snsouchstatic driver_t ic_driver = { 12238774Snsouch "ic", 12338774Snsouch ic_methods, 12438774Snsouch sizeof(struct ic_softc), 12538774Snsouch}; 12638774Snsouch 127181305Sjhbstatic void 128181305Sjhbic_alloc_buffers(struct ic_softc *sc, int mtu) 129181305Sjhb{ 130181305Sjhb char *obuf, *ifbuf; 131181305Sjhb 132181305Sjhb obuf = malloc(mtu + ICHDRLEN, M_DEVBUF, M_WAITOK); 133181305Sjhb ifbuf = malloc(mtu + ICHDRLEN, M_DEVBUF, M_WAITOK); 134181305Sjhb 135181305Sjhb mtx_lock(&sc->ic_lock); 136181305Sjhb while (sc->ic_flags & IC_BUFFERS_BUSY) { 137181305Sjhb sc->ic_flags |= IC_BUFFER_WAITER; 138181305Sjhb mtx_sleep(sc, &sc->ic_lock, 0, "icalloc", 0); 139181305Sjhb sc->ic_flags &= ~IC_BUFFER_WAITER; 140181305Sjhb } 141181305Sjhb 142181305Sjhb free(sc->ic_obuf, M_DEVBUF); 143181305Sjhb free(sc->ic_ifbuf, M_DEVBUF); 144181305Sjhb sc->ic_obuf = obuf; 145181305Sjhb sc->ic_ifbuf = ifbuf; 146181305Sjhb sc->ic_ifp->if_mtu = mtu; 147181305Sjhb mtx_unlock(&sc->ic_lock); 148181305Sjhb} 149181305Sjhb 15038774Snsouch/* 15138774Snsouch * icprobe() 15238774Snsouch */ 15338774Snsouchstatic int 15438774Snsouchicprobe(device_t dev) 15538774Snsouch{ 156186833Snwhitehorn return (BUS_PROBE_NOWILDCARD); 15738774Snsouch} 158181305Sjhb 15938774Snsouch/* 16038774Snsouch * icattach() 16138774Snsouch */ 16238774Snsouchstatic int 16338774Snsouchicattach(device_t dev) 16438774Snsouch{ 16538774Snsouch struct ic_softc *sc = (struct ic_softc *)device_get_softc(dev); 166147256Sbrooks struct ifnet *ifp; 16738774Snsouch 168147256Sbrooks ifp = sc->ic_ifp = if_alloc(IFT_PARA); 169157497Simp if (ifp == NULL) 170147256Sbrooks return (ENOSPC); 171147256Sbrooks 172181305Sjhb mtx_init(&sc->ic_lock, device_get_nameunit(dev), MTX_NETWORK_LOCK, 173181305Sjhb MTX_DEF); 17493023Snsouch sc->ic_addr = PCF_MASTER_ADDRESS; /* XXX only PCF masters */ 175181305Sjhb sc->ic_dev = dev; 17638774Snsouch 17738774Snsouch ifp->if_softc = sc; 178121816Sbrooks if_initname(ifp, device_get_name(dev), device_get_unit(dev)); 179181305Sjhb ifp->if_flags = IFF_SIMPLEX | IFF_POINTOPOINT | IFF_MULTICAST; 18038774Snsouch ifp->if_ioctl = icioctl; 18138774Snsouch ifp->if_output = icoutput; 18238774Snsouch ifp->if_hdrlen = 0; 18338774Snsouch ifp->if_addrlen = 0; 18438774Snsouch ifp->if_snd.ifq_maxlen = IFQ_MAXLEN; 18538774Snsouch 186181305Sjhb ic_alloc_buffers(sc, ICMTU); 187181305Sjhb 18838774Snsouch if_attach(ifp); 18938774Snsouch 19038774Snsouch bpfattach(ifp, DLT_NULL, ICHDRLEN); 19138774Snsouch 19238774Snsouch return (0); 19338774Snsouch} 19438774Snsouch 19538774Snsouch/* 19638774Snsouch * iciotcl() 19738774Snsouch */ 19838774Snsouchstatic int 19938774Snsouchicioctl(struct ifnet *ifp, u_long cmd, caddr_t data) 20038774Snsouch{ 201181305Sjhb struct ic_softc *sc = ifp->if_softc; 202181305Sjhb device_t icdev = sc->ic_dev; 203157497Simp device_t parent = device_get_parent(icdev); 204157497Simp struct ifaddr *ifa = (struct ifaddr *)data; 205157497Simp struct ifreq *ifr = (struct ifreq *)data; 206157497Simp int error; 20738774Snsouch 208157497Simp switch (cmd) { 20938774Snsouch 210157497Simp case SIOCSIFDSTADDR: 211157497Simp case SIOCAIFADDR: 212157497Simp case SIOCSIFADDR: 213157497Simp if (ifa->ifa_addr->sa_family != AF_INET) 214157497Simp return (EAFNOSUPPORT); 215181305Sjhb mtx_lock(&sc->ic_lock); 216157497Simp ifp->if_flags |= IFF_UP; 217181305Sjhb goto locked; 218157497Simp case SIOCSIFFLAGS: 219181305Sjhb mtx_lock(&sc->ic_lock); 220181305Sjhb locked: 221157497Simp if ((!(ifp->if_flags & IFF_UP)) && 222157497Simp (ifp->if_drv_flags & IFF_DRV_RUNNING)) { 22338774Snsouch 224157497Simp /* XXX disable PCF */ 225157497Simp ifp->if_drv_flags &= ~IFF_DRV_RUNNING; 226181305Sjhb mtx_unlock(&sc->ic_lock); 22738774Snsouch 228157497Simp /* IFF_UP is not set, try to release the bus anyway */ 229157497Simp iicbus_release_bus(parent, icdev); 230157497Simp break; 231157497Simp } 232157497Simp if (((ifp->if_flags & IFF_UP)) && 233157497Simp (!(ifp->if_drv_flags & IFF_DRV_RUNNING))) { 234181305Sjhb mtx_unlock(&sc->ic_lock); 235157497Simp if ((error = iicbus_request_bus(parent, icdev, 236157497Simp IIC_WAIT | IIC_INTR))) 237157497Simp return (error); 238181305Sjhb mtx_lock(&sc->ic_lock); 239157497Simp iicbus_reset(parent, IIC_FASTEST, 0, NULL); 240157497Simp ifp->if_drv_flags |= IFF_DRV_RUNNING; 241157497Simp } 242181305Sjhb mtx_unlock(&sc->ic_lock); 243157497Simp break; 24438774Snsouch 245157497Simp case SIOCSIFMTU: 246181305Sjhb ic_alloc_buffers(sc, ifr->ifr_mtu); 247157497Simp break; 24838774Snsouch 249157497Simp case SIOCGIFMTU: 250181305Sjhb mtx_lock(&sc->ic_lock); 251157497Simp ifr->ifr_mtu = sc->ic_ifp->if_mtu; 252181305Sjhb mtx_unlock(&sc->ic_lock); 253157497Simp break; 25438774Snsouch 255157497Simp case SIOCADDMULTI: 256157497Simp case SIOCDELMULTI: 257157497Simp if (ifr == 0) 258157497Simp return (EAFNOSUPPORT); /* XXX */ 259157497Simp switch (ifr->ifr_addr.sa_family) { 260157497Simp case AF_INET: 261157497Simp break; 262157497Simp default: 263157497Simp return (EAFNOSUPPORT); 264157497Simp } 265157497Simp break; 26638774Snsouch default: 267157497Simp return (EINVAL); 26838774Snsouch } 269157497Simp return (0); 27038774Snsouch} 27138774Snsouch 27238774Snsouch/* 27338774Snsouch * icintr() 27438774Snsouch */ 27538774Snsouchstatic void 276161516Simpicintr(device_t dev, int event, char *ptr) 27738774Snsouch{ 27838774Snsouch struct ic_softc *sc = (struct ic_softc *)device_get_softc(dev); 27938774Snsouch struct mbuf *top; 280181305Sjhb int len; 28138774Snsouch 282181305Sjhb mtx_lock(&sc->ic_lock); 283181305Sjhb 28438774Snsouch switch (event) { 28538774Snsouch 28638774Snsouch case INTR_GENERAL: 28738774Snsouch case INTR_START: 28838774Snsouch sc->ic_cp = sc->ic_ifbuf; 28938774Snsouch sc->ic_xfercnt = 0; 290181305Sjhb sc->ic_flags |= IC_IFBUF_BUSY; 29138774Snsouch break; 29238774Snsouch 29338774Snsouch case INTR_STOP: 29438774Snsouch 295157497Simp /* if any error occured during transfert, 296157497Simp * drop the packet */ 297181305Sjhb sc->ic_flags &= ~IC_IFBUF_BUSY; 298181305Sjhb if ((sc->ic_flags & (IC_BUFFERS_BUSY | IC_BUFFER_WAITER)) == 299181305Sjhb IC_BUFFER_WAITER) 300181305Sjhb wakeup(&sc); 301157497Simp if (sc->ic_iferrs) 302157497Simp goto err; 303157497Simp if ((len = sc->ic_xfercnt) == 0) 304157497Simp break; /* ignore */ 305157497Simp if (len <= ICHDRLEN) 306157497Simp goto err; 307157497Simp len -= ICHDRLEN; 308157497Simp sc->ic_ifp->if_ipackets++; 309157497Simp sc->ic_ifp->if_ibytes += len; 310157497Simp BPF_TAP(sc->ic_ifp, sc->ic_ifbuf, len + ICHDRLEN); 311157497Simp top = m_devget(sc->ic_ifbuf + ICHDRLEN, len, 0, sc->ic_ifp, 0); 312181305Sjhb if (top) { 313181305Sjhb mtx_unlock(&sc->ic_lock); 314157497Simp netisr_dispatch(NETISR_IP, top); 315181305Sjhb mtx_lock(&sc->ic_lock); 316181305Sjhb } 317157497Simp break; 31838774Snsouch err: 319181305Sjhb if_printf(sc->ic_ifp, "errors (%d)!\n", sc->ic_iferrs); 320157497Simp sc->ic_iferrs = 0; /* reset error count */ 321157497Simp sc->ic_ifp->if_ierrors++; 322157497Simp break; 32338774Snsouch 32438774Snsouch case INTR_RECEIVE: 325181305Sjhb if (sc->ic_xfercnt >= sc->ic_ifp->if_mtu + ICHDRLEN) { 326157497Simp sc->ic_iferrs++; 32738774Snsouch } else { 32838774Snsouch *sc->ic_cp++ = *ptr; 329157497Simp sc->ic_xfercnt++; 33038774Snsouch } 33138774Snsouch break; 33238774Snsouch 33338774Snsouch case INTR_NOACK: /* xfer terminated by master */ 33438774Snsouch break; 33538774Snsouch 33638774Snsouch case INTR_TRANSMIT: 33738774Snsouch *ptr = 0xff; /* XXX */ 33838774Snsouch break; 33938774Snsouch 34038774Snsouch case INTR_ERROR: 341157497Simp sc->ic_iferrs++; 34238774Snsouch break; 34338774Snsouch 34438774Snsouch default: 34587599Sobrien panic("%s: unknown event (%d)!", __func__, event); 34638774Snsouch } 34738774Snsouch 348181305Sjhb mtx_unlock(&sc->ic_lock); 34938774Snsouch return; 35038774Snsouch} 35138774Snsouch 35238774Snsouch/* 35338774Snsouch * icoutput() 35438774Snsouch */ 35538774Snsouchstatic int 356161516Simpicoutput(struct ifnet *ifp, struct mbuf *m, struct sockaddr *dst, 357161516Simp struct rtentry *rt) 35838774Snsouch{ 359181305Sjhb struct ic_softc *sc = ifp->if_softc; 360181305Sjhb device_t icdev = sc->ic_dev; 36138774Snsouch device_t parent = device_get_parent(icdev); 362181305Sjhb int len, sent; 36338774Snsouch struct mbuf *mm; 36438774Snsouch u_char *cp; 365147611Sdwmalone u_int32_t hdr; 36638774Snsouch 367147611Sdwmalone /* BPF writes need to be handled specially. */ 368147611Sdwmalone if (dst->sa_family == AF_UNSPEC) 369147611Sdwmalone bcopy(dst->sa_data, &hdr, sizeof(hdr)); 370147611Sdwmalone else 371147611Sdwmalone hdr = dst->sa_family; 372147611Sdwmalone 373181305Sjhb mtx_lock(&sc->ic_lock); 374148887Srwatson ifp->if_drv_flags |= IFF_DRV_RUNNING; 37538774Snsouch 37638774Snsouch /* already sending? */ 377181305Sjhb if (sc->ic_flags & IC_SENDING) { 378157497Simp ifp->if_oerrors++; 37938774Snsouch goto error; 38038774Snsouch } 38138774Snsouch 38238774Snsouch /* insert header */ 38338774Snsouch bcopy ((char *)&hdr, sc->ic_obuf, ICHDRLEN); 38438774Snsouch 38538774Snsouch cp = sc->ic_obuf + ICHDRLEN; 38638774Snsouch len = 0; 38738774Snsouch mm = m; 38838774Snsouch do { 389147256Sbrooks if (len + mm->m_len > sc->ic_ifp->if_mtu) { 390181305Sjhb /* packet too large */ 391157497Simp ifp->if_oerrors++; 39238774Snsouch goto error; 39338774Snsouch } 39438774Snsouch 39538774Snsouch bcopy(mtod(mm,char *), cp, mm->m_len); 39638774Snsouch cp += mm->m_len; 39738774Snsouch len += mm->m_len; 39838774Snsouch 39938774Snsouch } while ((mm = mm->m_next)); 40038774Snsouch 401123922Ssam BPF_MTAP2(ifp, &hdr, sizeof(hdr), m); 40238774Snsouch 403181305Sjhb sc->ic_flags |= (IC_SENDING | IC_OBUF_BUSY); 40438774Snsouch 40538774Snsouch m_freem(m); 406181305Sjhb mtx_unlock(&sc->ic_lock); 40738774Snsouch 40838774Snsouch /* send the packet */ 40938774Snsouch if (iicbus_block_write(parent, sc->ic_addr, sc->ic_obuf, 41038774Snsouch len + ICHDRLEN, &sent)) 41138774Snsouch 412157497Simp ifp->if_oerrors++; 41338774Snsouch else { 414157497Simp ifp->if_opackets++; 41538774Snsouch ifp->if_obytes += len; 416181305Sjhb } 41738774Snsouch 418181305Sjhb mtx_lock(&sc->ic_lock); 419181305Sjhb sc->ic_flags &= ~(IC_SENDING | IC_OBUF_BUSY); 420181305Sjhb if ((sc->ic_flags & (IC_BUFFERS_BUSY | IC_BUFFER_WAITER)) == 421181305Sjhb IC_BUFFER_WAITER) 422181305Sjhb wakeup(&sc); 423181305Sjhb mtx_unlock(&sc->ic_lock); 42438774Snsouch 42538774Snsouch return (0); 42638774Snsouch 42738774Snsoucherror: 42838774Snsouch m_freem(m); 429181305Sjhb mtx_unlock(&sc->ic_lock); 43038774Snsouch 43138774Snsouch return(0); 43238774Snsouch} 43338774Snsouch 43438774SnsouchDRIVER_MODULE(ic, iicbus, ic_driver, ic_devclass, 0, 0); 43593023SnsouchMODULE_DEPEND(ic, iicbus, IICBUS_MINVER, IICBUS_PREFVER, IICBUS_MAXVER); 43693023SnsouchMODULE_VERSION(ic, 1); 437