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: releng/10.3/sys/dev/iicbus/if_ic.c 255471 2013-09-11 09:19:44Z glebius $"); 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 <net/route.h> 5338774Snsouch#include <netinet/in.h> 5438774Snsouch#include <netinet/in_systm.h> 5538774Snsouch#include <netinet/in_var.h> 5638774Snsouch#include <netinet/ip.h> 5738774Snsouch#include <netinet/if_ether.h> 5838774Snsouch 5938774Snsouch#include <net/bpf.h> 6038774Snsouch 6138774Snsouch#include <dev/iicbus/iiconf.h> 6238774Snsouch#include <dev/iicbus/iicbus.h> 6338774Snsouch 6438774Snsouch#include "iicbus_if.h" 6538774Snsouch 6693023Snsouch#define PCF_MASTER_ADDRESS 0xaa 6793023Snsouch 68123922Ssam#define ICHDRLEN sizeof(u_int32_t) 6938774Snsouch#define ICMTU 1500 /* default mtu */ 7038774Snsouch 7138774Snsouchstruct ic_softc { 72147256Sbrooks struct ifnet *ic_ifp; 73181305Sjhb device_t ic_dev; 7438774Snsouch 7538774Snsouch u_char ic_addr; /* peer I2C address */ 7638774Snsouch 77181305Sjhb int ic_flags; 7838774Snsouch 7938774Snsouch char *ic_obuf; 8038774Snsouch char *ic_ifbuf; 8138774Snsouch char *ic_cp; 8238774Snsouch 8338774Snsouch int ic_xfercnt; 8438774Snsouch 8538774Snsouch int ic_iferrs; 86181305Sjhb 87181305Sjhb struct mtx ic_lock; 8838774Snsouch}; 8938774Snsouch 90181305Sjhb#define IC_SENDING 0x0001 91181305Sjhb#define IC_OBUF_BUSY 0x0002 92181305Sjhb#define IC_IFBUF_BUSY 0x0004 93181305Sjhb#define IC_BUFFERS_BUSY (IC_OBUF_BUSY | IC_IFBUF_BUSY) 94181305Sjhb#define IC_BUFFER_WAITER 0x0004 95181305Sjhb 9638774Snsouchstatic devclass_t ic_devclass; 9738774Snsouch 9838774Snsouchstatic int icprobe(device_t); 9938774Snsouchstatic int icattach(device_t); 10038774Snsouch 10138774Snsouchstatic int icioctl(struct ifnet *, u_long, caddr_t); 102249925Sglebiusstatic int icoutput(struct ifnet *, struct mbuf *, const struct sockaddr *, 103191148Skmacy struct route *); 10438774Snsouch 105188461Simpstatic int icintr(device_t, int, char *); 10638774Snsouch 10738774Snsouchstatic device_method_t ic_methods[] = { 10838774Snsouch /* device interface */ 10938774Snsouch DEVMETHOD(device_probe, icprobe), 11038774Snsouch DEVMETHOD(device_attach, icattach), 11138774Snsouch 11238774Snsouch /* iicbus interface */ 11338774Snsouch DEVMETHOD(iicbus_intr, icintr), 11438774Snsouch 11538774Snsouch { 0, 0 } 11638774Snsouch}; 11738774Snsouch 11838774Snsouchstatic driver_t ic_driver = { 11938774Snsouch "ic", 12038774Snsouch ic_methods, 12138774Snsouch sizeof(struct ic_softc), 12238774Snsouch}; 12338774Snsouch 124181305Sjhbstatic void 125181305Sjhbic_alloc_buffers(struct ic_softc *sc, int mtu) 126181305Sjhb{ 127181305Sjhb char *obuf, *ifbuf; 128181305Sjhb 129181305Sjhb obuf = malloc(mtu + ICHDRLEN, M_DEVBUF, M_WAITOK); 130181305Sjhb ifbuf = malloc(mtu + ICHDRLEN, M_DEVBUF, M_WAITOK); 131181305Sjhb 132181305Sjhb mtx_lock(&sc->ic_lock); 133181305Sjhb while (sc->ic_flags & IC_BUFFERS_BUSY) { 134181305Sjhb sc->ic_flags |= IC_BUFFER_WAITER; 135181305Sjhb mtx_sleep(sc, &sc->ic_lock, 0, "icalloc", 0); 136181305Sjhb sc->ic_flags &= ~IC_BUFFER_WAITER; 137181305Sjhb } 138181305Sjhb 139181305Sjhb free(sc->ic_obuf, M_DEVBUF); 140181305Sjhb free(sc->ic_ifbuf, M_DEVBUF); 141181305Sjhb sc->ic_obuf = obuf; 142181305Sjhb sc->ic_ifbuf = ifbuf; 143181305Sjhb sc->ic_ifp->if_mtu = mtu; 144181305Sjhb mtx_unlock(&sc->ic_lock); 145181305Sjhb} 146181305Sjhb 14738774Snsouch/* 14838774Snsouch * icprobe() 14938774Snsouch */ 15038774Snsouchstatic int 15138774Snsouchicprobe(device_t dev) 15238774Snsouch{ 153186833Snwhitehorn return (BUS_PROBE_NOWILDCARD); 15438774Snsouch} 155181305Sjhb 15638774Snsouch/* 15738774Snsouch * icattach() 15838774Snsouch */ 15938774Snsouchstatic int 16038774Snsouchicattach(device_t dev) 16138774Snsouch{ 16238774Snsouch struct ic_softc *sc = (struct ic_softc *)device_get_softc(dev); 163147256Sbrooks struct ifnet *ifp; 16438774Snsouch 165147256Sbrooks ifp = sc->ic_ifp = if_alloc(IFT_PARA); 166157497Simp if (ifp == NULL) 167147256Sbrooks return (ENOSPC); 168147256Sbrooks 169181305Sjhb mtx_init(&sc->ic_lock, device_get_nameunit(dev), MTX_NETWORK_LOCK, 170181305Sjhb MTX_DEF); 17193023Snsouch sc->ic_addr = PCF_MASTER_ADDRESS; /* XXX only PCF masters */ 172181305Sjhb sc->ic_dev = dev; 17338774Snsouch 17438774Snsouch ifp->if_softc = sc; 175121816Sbrooks if_initname(ifp, device_get_name(dev), device_get_unit(dev)); 176181305Sjhb ifp->if_flags = IFF_SIMPLEX | IFF_POINTOPOINT | IFF_MULTICAST; 17738774Snsouch ifp->if_ioctl = icioctl; 17838774Snsouch ifp->if_output = icoutput; 17938774Snsouch ifp->if_hdrlen = 0; 18038774Snsouch ifp->if_addrlen = 0; 181207554Ssobomax ifp->if_snd.ifq_maxlen = ifqmaxlen; 18238774Snsouch 183181305Sjhb ic_alloc_buffers(sc, ICMTU); 184181305Sjhb 18538774Snsouch if_attach(ifp); 18638774Snsouch 18738774Snsouch bpfattach(ifp, DLT_NULL, ICHDRLEN); 18838774Snsouch 18938774Snsouch return (0); 19038774Snsouch} 19138774Snsouch 19238774Snsouch/* 19338774Snsouch * iciotcl() 19438774Snsouch */ 19538774Snsouchstatic int 19638774Snsouchicioctl(struct ifnet *ifp, u_long cmd, caddr_t data) 19738774Snsouch{ 198181305Sjhb struct ic_softc *sc = ifp->if_softc; 199181305Sjhb device_t icdev = sc->ic_dev; 200157497Simp device_t parent = device_get_parent(icdev); 201157497Simp struct ifaddr *ifa = (struct ifaddr *)data; 202157497Simp struct ifreq *ifr = (struct ifreq *)data; 203157497Simp int error; 20438774Snsouch 205157497Simp switch (cmd) { 20638774Snsouch 207157497Simp case SIOCAIFADDR: 208157497Simp case SIOCSIFADDR: 209157497Simp if (ifa->ifa_addr->sa_family != AF_INET) 210157497Simp return (EAFNOSUPPORT); 211181305Sjhb mtx_lock(&sc->ic_lock); 212157497Simp ifp->if_flags |= IFF_UP; 213181305Sjhb goto locked; 214157497Simp case SIOCSIFFLAGS: 215181305Sjhb mtx_lock(&sc->ic_lock); 216181305Sjhb locked: 217157497Simp if ((!(ifp->if_flags & IFF_UP)) && 218157497Simp (ifp->if_drv_flags & IFF_DRV_RUNNING)) { 21938774Snsouch 220157497Simp /* XXX disable PCF */ 221157497Simp ifp->if_drv_flags &= ~IFF_DRV_RUNNING; 222181305Sjhb mtx_unlock(&sc->ic_lock); 22338774Snsouch 224157497Simp /* IFF_UP is not set, try to release the bus anyway */ 225157497Simp iicbus_release_bus(parent, icdev); 226157497Simp break; 227157497Simp } 228157497Simp if (((ifp->if_flags & IFF_UP)) && 229157497Simp (!(ifp->if_drv_flags & IFF_DRV_RUNNING))) { 230181305Sjhb mtx_unlock(&sc->ic_lock); 231157497Simp if ((error = iicbus_request_bus(parent, icdev, 232157497Simp IIC_WAIT | IIC_INTR))) 233157497Simp return (error); 234181305Sjhb mtx_lock(&sc->ic_lock); 235157497Simp iicbus_reset(parent, IIC_FASTEST, 0, NULL); 236157497Simp ifp->if_drv_flags |= IFF_DRV_RUNNING; 237157497Simp } 238181305Sjhb mtx_unlock(&sc->ic_lock); 239157497Simp break; 24038774Snsouch 241157497Simp case SIOCSIFMTU: 242181305Sjhb ic_alloc_buffers(sc, ifr->ifr_mtu); 243157497Simp break; 24438774Snsouch 245157497Simp case SIOCGIFMTU: 246181305Sjhb mtx_lock(&sc->ic_lock); 247157497Simp ifr->ifr_mtu = sc->ic_ifp->if_mtu; 248181305Sjhb mtx_unlock(&sc->ic_lock); 249157497Simp break; 25038774Snsouch 251157497Simp case SIOCADDMULTI: 252157497Simp case SIOCDELMULTI: 253157497Simp if (ifr == 0) 254157497Simp return (EAFNOSUPPORT); /* XXX */ 255157497Simp switch (ifr->ifr_addr.sa_family) { 256157497Simp case AF_INET: 257157497Simp break; 258157497Simp default: 259157497Simp return (EAFNOSUPPORT); 260157497Simp } 261157497Simp break; 26238774Snsouch default: 263157497Simp return (EINVAL); 26438774Snsouch } 265157497Simp return (0); 26638774Snsouch} 26738774Snsouch 26838774Snsouch/* 26938774Snsouch * icintr() 27038774Snsouch */ 271188461Simpstatic int 272161516Simpicintr(device_t dev, int event, char *ptr) 27338774Snsouch{ 27438774Snsouch struct ic_softc *sc = (struct ic_softc *)device_get_softc(dev); 27538774Snsouch struct mbuf *top; 276181305Sjhb int len; 27738774Snsouch 278181305Sjhb mtx_lock(&sc->ic_lock); 279181305Sjhb 28038774Snsouch switch (event) { 28138774Snsouch 28238774Snsouch case INTR_GENERAL: 28338774Snsouch case INTR_START: 28438774Snsouch sc->ic_cp = sc->ic_ifbuf; 28538774Snsouch sc->ic_xfercnt = 0; 286181305Sjhb sc->ic_flags |= IC_IFBUF_BUSY; 28738774Snsouch break; 28838774Snsouch 28938774Snsouch case INTR_STOP: 29038774Snsouch 291157497Simp /* if any error occured during transfert, 292157497Simp * drop the packet */ 293181305Sjhb sc->ic_flags &= ~IC_IFBUF_BUSY; 294181305Sjhb if ((sc->ic_flags & (IC_BUFFERS_BUSY | IC_BUFFER_WAITER)) == 295181305Sjhb IC_BUFFER_WAITER) 296181305Sjhb wakeup(&sc); 297157497Simp if (sc->ic_iferrs) 298157497Simp goto err; 299157497Simp if ((len = sc->ic_xfercnt) == 0) 300157497Simp break; /* ignore */ 301157497Simp if (len <= ICHDRLEN) 302157497Simp goto err; 303157497Simp len -= ICHDRLEN; 304157497Simp sc->ic_ifp->if_ipackets++; 305157497Simp sc->ic_ifp->if_ibytes += len; 306157497Simp BPF_TAP(sc->ic_ifp, sc->ic_ifbuf, len + ICHDRLEN); 307157497Simp top = m_devget(sc->ic_ifbuf + ICHDRLEN, len, 0, sc->ic_ifp, 0); 308181305Sjhb if (top) { 309181305Sjhb mtx_unlock(&sc->ic_lock); 310223741Sbz M_SETFIB(top, sc->ic_ifp->if_fib); 311157497Simp netisr_dispatch(NETISR_IP, top); 312181305Sjhb mtx_lock(&sc->ic_lock); 313181305Sjhb } 314157497Simp break; 31538774Snsouch err: 316181305Sjhb if_printf(sc->ic_ifp, "errors (%d)!\n", sc->ic_iferrs); 317157497Simp sc->ic_iferrs = 0; /* reset error count */ 318157497Simp sc->ic_ifp->if_ierrors++; 319157497Simp break; 32038774Snsouch 32138774Snsouch case INTR_RECEIVE: 322181305Sjhb if (sc->ic_xfercnt >= sc->ic_ifp->if_mtu + ICHDRLEN) { 323157497Simp sc->ic_iferrs++; 32438774Snsouch } else { 32538774Snsouch *sc->ic_cp++ = *ptr; 326157497Simp sc->ic_xfercnt++; 32738774Snsouch } 32838774Snsouch break; 32938774Snsouch 33038774Snsouch case INTR_NOACK: /* xfer terminated by master */ 33138774Snsouch break; 33238774Snsouch 33338774Snsouch case INTR_TRANSMIT: 33438774Snsouch *ptr = 0xff; /* XXX */ 33538774Snsouch break; 33638774Snsouch 33738774Snsouch case INTR_ERROR: 338157497Simp sc->ic_iferrs++; 33938774Snsouch break; 34038774Snsouch 34138774Snsouch default: 34287599Sobrien panic("%s: unknown event (%d)!", __func__, event); 34338774Snsouch } 34438774Snsouch 345181305Sjhb mtx_unlock(&sc->ic_lock); 346188461Simp return (0); 34738774Snsouch} 34838774Snsouch 34938774Snsouch/* 35038774Snsouch * icoutput() 35138774Snsouch */ 35238774Snsouchstatic int 353249925Sglebiusicoutput(struct ifnet *ifp, struct mbuf *m, const struct sockaddr *dst, 354191148Skmacy struct route *ro) 35538774Snsouch{ 356181305Sjhb struct ic_softc *sc = ifp->if_softc; 357181305Sjhb device_t icdev = sc->ic_dev; 35838774Snsouch device_t parent = device_get_parent(icdev); 359181305Sjhb int len, sent; 36038774Snsouch struct mbuf *mm; 36138774Snsouch u_char *cp; 362147611Sdwmalone u_int32_t hdr; 36338774Snsouch 364147611Sdwmalone /* BPF writes need to be handled specially. */ 365147611Sdwmalone if (dst->sa_family == AF_UNSPEC) 366147611Sdwmalone bcopy(dst->sa_data, &hdr, sizeof(hdr)); 367147611Sdwmalone else 368147611Sdwmalone hdr = dst->sa_family; 369147611Sdwmalone 370181305Sjhb mtx_lock(&sc->ic_lock); 371148887Srwatson ifp->if_drv_flags |= IFF_DRV_RUNNING; 37238774Snsouch 37338774Snsouch /* already sending? */ 374181305Sjhb if (sc->ic_flags & IC_SENDING) { 375157497Simp ifp->if_oerrors++; 37638774Snsouch goto error; 37738774Snsouch } 37838774Snsouch 37938774Snsouch /* insert header */ 38038774Snsouch bcopy ((char *)&hdr, sc->ic_obuf, ICHDRLEN); 38138774Snsouch 38238774Snsouch cp = sc->ic_obuf + ICHDRLEN; 38338774Snsouch len = 0; 38438774Snsouch mm = m; 38538774Snsouch do { 386147256Sbrooks if (len + mm->m_len > sc->ic_ifp->if_mtu) { 387181305Sjhb /* packet too large */ 388157497Simp ifp->if_oerrors++; 38938774Snsouch goto error; 39038774Snsouch } 39138774Snsouch 39238774Snsouch bcopy(mtod(mm,char *), cp, mm->m_len); 39338774Snsouch cp += mm->m_len; 39438774Snsouch len += mm->m_len; 39538774Snsouch 39638774Snsouch } while ((mm = mm->m_next)); 39738774Snsouch 398123922Ssam BPF_MTAP2(ifp, &hdr, sizeof(hdr), m); 39938774Snsouch 400181305Sjhb sc->ic_flags |= (IC_SENDING | IC_OBUF_BUSY); 40138774Snsouch 40238774Snsouch m_freem(m); 403181305Sjhb mtx_unlock(&sc->ic_lock); 40438774Snsouch 40538774Snsouch /* send the packet */ 40638774Snsouch if (iicbus_block_write(parent, sc->ic_addr, sc->ic_obuf, 40738774Snsouch len + ICHDRLEN, &sent)) 40838774Snsouch 409157497Simp ifp->if_oerrors++; 41038774Snsouch else { 411157497Simp ifp->if_opackets++; 41238774Snsouch ifp->if_obytes += len; 413181305Sjhb } 41438774Snsouch 415181305Sjhb mtx_lock(&sc->ic_lock); 416181305Sjhb sc->ic_flags &= ~(IC_SENDING | IC_OBUF_BUSY); 417181305Sjhb if ((sc->ic_flags & (IC_BUFFERS_BUSY | IC_BUFFER_WAITER)) == 418181305Sjhb IC_BUFFER_WAITER) 419181305Sjhb wakeup(&sc); 420181305Sjhb mtx_unlock(&sc->ic_lock); 42138774Snsouch 42238774Snsouch return (0); 42338774Snsouch 42438774Snsoucherror: 42538774Snsouch m_freem(m); 426181305Sjhb mtx_unlock(&sc->ic_lock); 42738774Snsouch 42838774Snsouch return(0); 42938774Snsouch} 43038774Snsouch 43138774SnsouchDRIVER_MODULE(ic, iicbus, ic_driver, ic_devclass, 0, 0); 43293023SnsouchMODULE_DEPEND(ic, iicbus, IICBUS_MINVER, IICBUS_PREFVER, IICBUS_MAXVER); 43393023SnsouchMODULE_VERSION(ic, 1); 434