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: stable/11/sys/dev/iicbus/if_ic.c 315221 2017-03-14 02:06:03Z pfg $"); 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> 49257176Sglebius#include <net/if_var.h> 5038774Snsouch#include <net/if_types.h> 5138774Snsouch#include <net/netisr.h> 5238774Snsouch 5338774Snsouch#include <net/route.h> 5438774Snsouch#include <netinet/in.h> 5538774Snsouch#include <netinet/in_systm.h> 5638774Snsouch#include <netinet/in_var.h> 5738774Snsouch#include <netinet/ip.h> 5838774Snsouch#include <netinet/if_ether.h> 5938774Snsouch 6038774Snsouch#include <net/bpf.h> 6138774Snsouch 6238774Snsouch#include <dev/iicbus/iiconf.h> 6338774Snsouch#include <dev/iicbus/iicbus.h> 6438774Snsouch 6538774Snsouch#include "iicbus_if.h" 6638774Snsouch 6793023Snsouch#define PCF_MASTER_ADDRESS 0xaa 6893023Snsouch 69123922Ssam#define ICHDRLEN sizeof(u_int32_t) 7038774Snsouch#define ICMTU 1500 /* default mtu */ 7138774Snsouch 7238774Snsouchstruct ic_softc { 73147256Sbrooks struct ifnet *ic_ifp; 74181305Sjhb device_t ic_dev; 7538774Snsouch 7638774Snsouch u_char ic_addr; /* peer I2C address */ 7738774Snsouch 78181305Sjhb int ic_flags; 7938774Snsouch 8038774Snsouch char *ic_obuf; 8138774Snsouch char *ic_ifbuf; 8238774Snsouch char *ic_cp; 8338774Snsouch 8438774Snsouch int ic_xfercnt; 8538774Snsouch 8638774Snsouch int ic_iferrs; 87181305Sjhb 88181305Sjhb struct mtx ic_lock; 8938774Snsouch}; 9038774Snsouch 91181305Sjhb#define IC_SENDING 0x0001 92181305Sjhb#define IC_OBUF_BUSY 0x0002 93181305Sjhb#define IC_IFBUF_BUSY 0x0004 94181305Sjhb#define IC_BUFFERS_BUSY (IC_OBUF_BUSY | IC_IFBUF_BUSY) 95181305Sjhb#define IC_BUFFER_WAITER 0x0004 96181305Sjhb 9738774Snsouchstatic devclass_t ic_devclass; 9838774Snsouch 9938774Snsouchstatic int icprobe(device_t); 10038774Snsouchstatic int icattach(device_t); 10138774Snsouch 10238774Snsouchstatic int icioctl(struct ifnet *, u_long, caddr_t); 103249925Sglebiusstatic int icoutput(struct ifnet *, struct mbuf *, const struct sockaddr *, 104191148Skmacy struct route *); 10538774Snsouch 106188461Simpstatic int icintr(device_t, int, char *); 10738774Snsouch 10838774Snsouchstatic device_method_t ic_methods[] = { 10938774Snsouch /* device interface */ 11038774Snsouch DEVMETHOD(device_probe, icprobe), 11138774Snsouch DEVMETHOD(device_attach, icattach), 11238774Snsouch 11338774Snsouch /* iicbus interface */ 11438774Snsouch DEVMETHOD(iicbus_intr, icintr), 11538774Snsouch 11638774Snsouch { 0, 0 } 11738774Snsouch}; 11838774Snsouch 11938774Snsouchstatic driver_t ic_driver = { 12038774Snsouch "ic", 12138774Snsouch ic_methods, 12238774Snsouch sizeof(struct ic_softc), 12338774Snsouch}; 12438774Snsouch 125181305Sjhbstatic void 126181305Sjhbic_alloc_buffers(struct ic_softc *sc, int mtu) 127181305Sjhb{ 128181305Sjhb char *obuf, *ifbuf; 129181305Sjhb 130181305Sjhb obuf = malloc(mtu + ICHDRLEN, M_DEVBUF, M_WAITOK); 131181305Sjhb ifbuf = malloc(mtu + ICHDRLEN, M_DEVBUF, M_WAITOK); 132181305Sjhb 133181305Sjhb mtx_lock(&sc->ic_lock); 134181305Sjhb while (sc->ic_flags & IC_BUFFERS_BUSY) { 135181305Sjhb sc->ic_flags |= IC_BUFFER_WAITER; 136181305Sjhb mtx_sleep(sc, &sc->ic_lock, 0, "icalloc", 0); 137181305Sjhb sc->ic_flags &= ~IC_BUFFER_WAITER; 138181305Sjhb } 139181305Sjhb 140181305Sjhb free(sc->ic_obuf, M_DEVBUF); 141181305Sjhb free(sc->ic_ifbuf, M_DEVBUF); 142181305Sjhb sc->ic_obuf = obuf; 143181305Sjhb sc->ic_ifbuf = ifbuf; 144181305Sjhb sc->ic_ifp->if_mtu = mtu; 145181305Sjhb mtx_unlock(&sc->ic_lock); 146181305Sjhb} 147181305Sjhb 14838774Snsouch/* 14938774Snsouch * icprobe() 15038774Snsouch */ 15138774Snsouchstatic int 15238774Snsouchicprobe(device_t dev) 15338774Snsouch{ 154186833Snwhitehorn return (BUS_PROBE_NOWILDCARD); 15538774Snsouch} 156181305Sjhb 15738774Snsouch/* 15838774Snsouch * icattach() 15938774Snsouch */ 16038774Snsouchstatic int 16138774Snsouchicattach(device_t dev) 16238774Snsouch{ 16338774Snsouch struct ic_softc *sc = (struct ic_softc *)device_get_softc(dev); 164147256Sbrooks struct ifnet *ifp; 16538774Snsouch 166147256Sbrooks ifp = sc->ic_ifp = if_alloc(IFT_PARA); 167157497Simp if (ifp == NULL) 168147256Sbrooks return (ENOSPC); 169147256Sbrooks 170181305Sjhb mtx_init(&sc->ic_lock, device_get_nameunit(dev), MTX_NETWORK_LOCK, 171181305Sjhb MTX_DEF); 17293023Snsouch sc->ic_addr = PCF_MASTER_ADDRESS; /* XXX only PCF masters */ 173181305Sjhb sc->ic_dev = dev; 17438774Snsouch 17538774Snsouch ifp->if_softc = sc; 176121816Sbrooks if_initname(ifp, device_get_name(dev), device_get_unit(dev)); 177181305Sjhb ifp->if_flags = IFF_SIMPLEX | IFF_POINTOPOINT | IFF_MULTICAST; 17838774Snsouch ifp->if_ioctl = icioctl; 17938774Snsouch ifp->if_output = icoutput; 18038774Snsouch ifp->if_hdrlen = 0; 18138774Snsouch ifp->if_addrlen = 0; 182207554Ssobomax ifp->if_snd.ifq_maxlen = ifqmaxlen; 18338774Snsouch 184181305Sjhb ic_alloc_buffers(sc, ICMTU); 185181305Sjhb 18638774Snsouch if_attach(ifp); 18738774Snsouch 18838774Snsouch bpfattach(ifp, DLT_NULL, ICHDRLEN); 18938774Snsouch 19038774Snsouch return (0); 19138774Snsouch} 19238774Snsouch 19338774Snsouch/* 19438774Snsouch * iciotcl() 19538774Snsouch */ 19638774Snsouchstatic int 19738774Snsouchicioctl(struct ifnet *ifp, u_long cmd, caddr_t data) 19838774Snsouch{ 199181305Sjhb struct ic_softc *sc = ifp->if_softc; 200181305Sjhb device_t icdev = sc->ic_dev; 201157497Simp device_t parent = device_get_parent(icdev); 202157497Simp struct ifaddr *ifa = (struct ifaddr *)data; 203157497Simp struct ifreq *ifr = (struct ifreq *)data; 204157497Simp int error; 20538774Snsouch 206157497Simp switch (cmd) { 20738774Snsouch 208157497Simp case SIOCAIFADDR: 209157497Simp case SIOCSIFADDR: 210157497Simp if (ifa->ifa_addr->sa_family != AF_INET) 211157497Simp return (EAFNOSUPPORT); 212181305Sjhb mtx_lock(&sc->ic_lock); 213157497Simp ifp->if_flags |= IFF_UP; 214181305Sjhb goto locked; 215157497Simp case SIOCSIFFLAGS: 216181305Sjhb mtx_lock(&sc->ic_lock); 217181305Sjhb locked: 218157497Simp if ((!(ifp->if_flags & IFF_UP)) && 219157497Simp (ifp->if_drv_flags & IFF_DRV_RUNNING)) { 22038774Snsouch 221157497Simp /* XXX disable PCF */ 222157497Simp ifp->if_drv_flags &= ~IFF_DRV_RUNNING; 223181305Sjhb mtx_unlock(&sc->ic_lock); 22438774Snsouch 225157497Simp /* IFF_UP is not set, try to release the bus anyway */ 226157497Simp iicbus_release_bus(parent, icdev); 227157497Simp break; 228157497Simp } 229157497Simp if (((ifp->if_flags & IFF_UP)) && 230157497Simp (!(ifp->if_drv_flags & IFF_DRV_RUNNING))) { 231181305Sjhb mtx_unlock(&sc->ic_lock); 232157497Simp if ((error = iicbus_request_bus(parent, icdev, 233157497Simp IIC_WAIT | IIC_INTR))) 234157497Simp return (error); 235181305Sjhb mtx_lock(&sc->ic_lock); 236157497Simp iicbus_reset(parent, IIC_FASTEST, 0, NULL); 237157497Simp ifp->if_drv_flags |= IFF_DRV_RUNNING; 238157497Simp } 239181305Sjhb mtx_unlock(&sc->ic_lock); 240157497Simp break; 24138774Snsouch 242157497Simp case SIOCSIFMTU: 243181305Sjhb ic_alloc_buffers(sc, ifr->ifr_mtu); 244157497Simp break; 24538774Snsouch 246157497Simp case SIOCGIFMTU: 247181305Sjhb mtx_lock(&sc->ic_lock); 248157497Simp ifr->ifr_mtu = sc->ic_ifp->if_mtu; 249181305Sjhb mtx_unlock(&sc->ic_lock); 250157497Simp break; 25138774Snsouch 252157497Simp case SIOCADDMULTI: 253157497Simp case SIOCDELMULTI: 254315221Spfg if (ifr == NULL) 255157497Simp return (EAFNOSUPPORT); /* XXX */ 256157497Simp switch (ifr->ifr_addr.sa_family) { 257157497Simp case AF_INET: 258157497Simp break; 259157497Simp default: 260157497Simp return (EAFNOSUPPORT); 261157497Simp } 262157497Simp break; 26338774Snsouch default: 264157497Simp return (EINVAL); 26538774Snsouch } 266157497Simp return (0); 26738774Snsouch} 26838774Snsouch 26938774Snsouch/* 27038774Snsouch * icintr() 27138774Snsouch */ 272188461Simpstatic int 273161516Simpicintr(device_t dev, int event, char *ptr) 27438774Snsouch{ 27538774Snsouch struct ic_softc *sc = (struct ic_softc *)device_get_softc(dev); 27638774Snsouch struct mbuf *top; 277181305Sjhb int len; 27838774Snsouch 279181305Sjhb mtx_lock(&sc->ic_lock); 280181305Sjhb 28138774Snsouch switch (event) { 28238774Snsouch 28338774Snsouch case INTR_GENERAL: 28438774Snsouch case INTR_START: 28538774Snsouch sc->ic_cp = sc->ic_ifbuf; 28638774Snsouch sc->ic_xfercnt = 0; 287181305Sjhb sc->ic_flags |= IC_IFBUF_BUSY; 28838774Snsouch break; 28938774Snsouch 29038774Snsouch case INTR_STOP: 29138774Snsouch 292298955Spfg /* if any error occurred during transfert, 293157497Simp * drop the packet */ 294181305Sjhb sc->ic_flags &= ~IC_IFBUF_BUSY; 295181305Sjhb if ((sc->ic_flags & (IC_BUFFERS_BUSY | IC_BUFFER_WAITER)) == 296181305Sjhb IC_BUFFER_WAITER) 297181305Sjhb wakeup(&sc); 298157497Simp if (sc->ic_iferrs) 299157497Simp goto err; 300157497Simp if ((len = sc->ic_xfercnt) == 0) 301157497Simp break; /* ignore */ 302157497Simp if (len <= ICHDRLEN) 303157497Simp goto err; 304157497Simp len -= ICHDRLEN; 305271849Sglebius if_inc_counter(sc->ic_ifp, IFCOUNTER_IPACKETS, 1); 306271849Sglebius if_inc_counter(sc->ic_ifp, IFCOUNTER_IBYTES, len); 307157497Simp BPF_TAP(sc->ic_ifp, sc->ic_ifbuf, len + ICHDRLEN); 308157497Simp top = m_devget(sc->ic_ifbuf + ICHDRLEN, len, 0, sc->ic_ifp, 0); 309181305Sjhb if (top) { 310181305Sjhb mtx_unlock(&sc->ic_lock); 311223741Sbz M_SETFIB(top, sc->ic_ifp->if_fib); 312157497Simp netisr_dispatch(NETISR_IP, top); 313181305Sjhb mtx_lock(&sc->ic_lock); 314181305Sjhb } 315157497Simp break; 31638774Snsouch err: 317181305Sjhb if_printf(sc->ic_ifp, "errors (%d)!\n", sc->ic_iferrs); 318157497Simp sc->ic_iferrs = 0; /* reset error count */ 319271849Sglebius if_inc_counter(sc->ic_ifp, IFCOUNTER_IERRORS, 1); 320157497Simp break; 32138774Snsouch 32238774Snsouch case INTR_RECEIVE: 323181305Sjhb if (sc->ic_xfercnt >= sc->ic_ifp->if_mtu + ICHDRLEN) { 324157497Simp sc->ic_iferrs++; 32538774Snsouch } else { 32638774Snsouch *sc->ic_cp++ = *ptr; 327157497Simp sc->ic_xfercnt++; 32838774Snsouch } 32938774Snsouch break; 33038774Snsouch 33138774Snsouch case INTR_NOACK: /* xfer terminated by master */ 33238774Snsouch break; 33338774Snsouch 33438774Snsouch case INTR_TRANSMIT: 33538774Snsouch *ptr = 0xff; /* XXX */ 33638774Snsouch break; 33738774Snsouch 33838774Snsouch case INTR_ERROR: 339157497Simp sc->ic_iferrs++; 34038774Snsouch break; 34138774Snsouch 34238774Snsouch default: 34387599Sobrien panic("%s: unknown event (%d)!", __func__, event); 34438774Snsouch } 34538774Snsouch 346181305Sjhb mtx_unlock(&sc->ic_lock); 347188461Simp return (0); 34838774Snsouch} 34938774Snsouch 35038774Snsouch/* 35138774Snsouch * icoutput() 35238774Snsouch */ 35338774Snsouchstatic int 354249925Sglebiusicoutput(struct ifnet *ifp, struct mbuf *m, const struct sockaddr *dst, 355191148Skmacy struct route *ro) 35638774Snsouch{ 357181305Sjhb struct ic_softc *sc = ifp->if_softc; 358181305Sjhb device_t icdev = sc->ic_dev; 35938774Snsouch device_t parent = device_get_parent(icdev); 360181305Sjhb int len, sent; 36138774Snsouch struct mbuf *mm; 36238774Snsouch u_char *cp; 363147611Sdwmalone u_int32_t hdr; 36438774Snsouch 365147611Sdwmalone /* BPF writes need to be handled specially. */ 366147611Sdwmalone if (dst->sa_family == AF_UNSPEC) 367147611Sdwmalone bcopy(dst->sa_data, &hdr, sizeof(hdr)); 368147611Sdwmalone else 369147611Sdwmalone hdr = dst->sa_family; 370147611Sdwmalone 371181305Sjhb mtx_lock(&sc->ic_lock); 372148887Srwatson ifp->if_drv_flags |= IFF_DRV_RUNNING; 37338774Snsouch 37438774Snsouch /* already sending? */ 375181305Sjhb if (sc->ic_flags & IC_SENDING) { 376271849Sglebius if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); 37738774Snsouch goto error; 37838774Snsouch } 37938774Snsouch 38038774Snsouch /* insert header */ 38138774Snsouch bcopy ((char *)&hdr, sc->ic_obuf, ICHDRLEN); 38238774Snsouch 38338774Snsouch cp = sc->ic_obuf + ICHDRLEN; 38438774Snsouch len = 0; 38538774Snsouch mm = m; 38638774Snsouch do { 387147256Sbrooks if (len + mm->m_len > sc->ic_ifp->if_mtu) { 388181305Sjhb /* packet too large */ 389271849Sglebius if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); 39038774Snsouch goto error; 39138774Snsouch } 39238774Snsouch 39338774Snsouch bcopy(mtod(mm,char *), cp, mm->m_len); 39438774Snsouch cp += mm->m_len; 39538774Snsouch len += mm->m_len; 39638774Snsouch 39738774Snsouch } while ((mm = mm->m_next)); 39838774Snsouch 399123922Ssam BPF_MTAP2(ifp, &hdr, sizeof(hdr), m); 40038774Snsouch 401181305Sjhb sc->ic_flags |= (IC_SENDING | IC_OBUF_BUSY); 40238774Snsouch 40338774Snsouch m_freem(m); 404181305Sjhb mtx_unlock(&sc->ic_lock); 40538774Snsouch 40638774Snsouch /* send the packet */ 40738774Snsouch if (iicbus_block_write(parent, sc->ic_addr, sc->ic_obuf, 40838774Snsouch len + ICHDRLEN, &sent)) 40938774Snsouch 410271849Sglebius if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); 41138774Snsouch else { 412271849Sglebius if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1); 413271849Sglebius if_inc_counter(ifp, IFCOUNTER_OBYTES, len); 414181305Sjhb } 41538774Snsouch 416181305Sjhb mtx_lock(&sc->ic_lock); 417181305Sjhb sc->ic_flags &= ~(IC_SENDING | IC_OBUF_BUSY); 418181305Sjhb if ((sc->ic_flags & (IC_BUFFERS_BUSY | IC_BUFFER_WAITER)) == 419181305Sjhb IC_BUFFER_WAITER) 420181305Sjhb wakeup(&sc); 421181305Sjhb mtx_unlock(&sc->ic_lock); 42238774Snsouch 42338774Snsouch return (0); 42438774Snsouch 42538774Snsoucherror: 42638774Snsouch m_freem(m); 427181305Sjhb mtx_unlock(&sc->ic_lock); 42838774Snsouch 42938774Snsouch return(0); 43038774Snsouch} 43138774Snsouch 43238774SnsouchDRIVER_MODULE(ic, iicbus, ic_driver, ic_devclass, 0, 0); 43393023SnsouchMODULE_DEPEND(ic, iicbus, IICBUS_MINVER, IICBUS_PREFVER, IICBUS_MAXVER); 43493023SnsouchMODULE_VERSION(ic, 1); 435