| Commit | Line | Data |
|---|---|---|
| 984263bc MD |
1 | /* |
| 2 | * Copyright (c) 2002-2003 | |
| 3 | * Hidetoshi Shimokawa. 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 | * 3. All advertising materials mentioning features or use of this software | |
| 14 | * must display the following acknowledgement: | |
| 15 | * | |
| 16 | * This product includes software developed by Hidetoshi Shimokawa. | |
| 17 | * | |
| 18 | * 4. Neither the name of the author nor the names of its contributors | |
| 19 | * may be used to endorse or promote products derived from this software | |
| 20 | * without specific prior written permission. | |
| 21 | * | |
| 22 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | |
| 23 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
| 24 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
| 25 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | |
| 26 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
| 27 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
| 28 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
| 29 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
| 30 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
| 31 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
| 32 | * SUCH DAMAGE. | |
| 33 | * | |
| 78748771 | 34 | * $FreeBSD: src/sys/dev/firewire/if_fwe.c,v 1.27 2004/01/08 14:58:09 simokawa Exp $ |
| 9db4b353 | 35 | * $DragonFly: src/sys/dev/netif/fwe/if_fwe.c,v 1.31 2008/05/14 11:59:20 sephe Exp $ |
| 984263bc MD |
36 | */ |
| 37 | ||
| 38 | #include "opt_inet.h" | |
| 2b71c8f1 | 39 | #include "opt_polling.h" |
| 984263bc MD |
40 | |
| 41 | #include <sys/param.h> | |
| 42 | #include <sys/conf.h> | |
| 43 | #include <sys/kernel.h> | |
| 44 | #include <sys/malloc.h> | |
| 45 | #include <sys/mbuf.h> | |
| 46 | #include <sys/socket.h> | |
| 47 | #include <sys/sockio.h> | |
| 48 | #include <sys/sysctl.h> | |
| 49 | #include <sys/systm.h> | |
| 50 | #include <sys/module.h> | |
| 51 | #include <sys/bus.h> | |
| 07f992ed | 52 | #include <sys/thread2.h> |
| 984263bc MD |
53 | |
| 54 | #include <net/bpf.h> | |
| 55 | #include <net/ethernet.h> | |
| 56 | #include <net/if.h> | |
| 57 | #include <net/if_arp.h> | |
| 78748771 | 58 | #ifdef __DragonFly__ |
| efa919fe | 59 | #include <net/ifq_var.h> |
| 1f2de5d4 | 60 | #include <net/vlan/if_vlan_var.h> |
| 1f2de5d4 MD |
61 | #include <bus/firewire/firewire.h> |
| 62 | #include <bus/firewire/firewirereg.h> | |
| 63 | #include "if_fwevar.h" | |
| 78748771 JS |
64 | #else |
| 65 | #include <net/if_vlan_var.h> | |
| 66 | ||
| 67 | #include <dev/firewire/firewire.h> | |
| 68 | #include <dev/firewire/firewirereg.h> | |
| 69 | #include <dev/firewire/if_fwevar.h> | |
| 70 | #endif | |
| 984263bc | 71 | |
| 78748771 | 72 | #define FWEDEBUG if (fwedebug) if_printf |
| 984263bc | 73 | #define TX_MAX_QUEUE (FWMAXQUEUE - 1) |
| 984263bc MD |
74 | |
| 75 | /* network interface */ | |
| b5101a88 | 76 | static void fwe_start (struct ifnet *); |
| bd4539cc | 77 | static int fwe_ioctl (struct ifnet *, u_long, caddr_t, struct ucred *); |
| b5101a88 | 78 | static void fwe_init (void *); |
| 984263bc | 79 | |
| b5101a88 RG |
80 | static void fwe_output_callback (struct fw_xfer *); |
| 81 | static void fwe_as_output (struct fwe_softc *, struct ifnet *); | |
| 82 | static void fwe_as_input (struct fw_xferq *); | |
| 984263bc MD |
83 | |
| 84 | static int fwedebug = 0; | |
| 85 | static int stream_ch = 1; | |
| 78748771 JS |
86 | static int tx_speed = 2; |
| 87 | static int rx_queue_len = FWMAXQUEUE; | |
| 984263bc MD |
88 | |
| 89 | MALLOC_DEFINE(M_FWE, "if_fwe", "Ethernet over FireWire interface"); | |
| 0c52fa62 SG |
90 | SYSCTL_INT(_debug, OID_AUTO, if_fwe_debug, CTLFLAG_RW, &fwedebug, 0, |
| 91 | "Enable debug output"); | |
| 984263bc MD |
92 | SYSCTL_DECL(_hw_firewire); |
| 93 | SYSCTL_NODE(_hw_firewire, OID_AUTO, fwe, CTLFLAG_RD, 0, | |
| 78748771 | 94 | "Ethernet emulation subsystem"); |
| 984263bc MD |
95 | SYSCTL_INT(_hw_firewire_fwe, OID_AUTO, stream_ch, CTLFLAG_RW, &stream_ch, 0, |
| 96 | "Stream channel to use"); | |
| 78748771 JS |
97 | SYSCTL_INT(_hw_firewire_fwe, OID_AUTO, tx_speed, CTLFLAG_RW, &tx_speed, 0, |
| 98 | "Transmission speed"); | |
| 99 | SYSCTL_INT(_hw_firewire_fwe, OID_AUTO, rx_queue_len, CTLFLAG_RW, &rx_queue_len, | |
| 100 | 0, "Length of the receive queue"); | |
| 101 | ||
| 102 | TUNABLE_INT("hw.firewire.fwe.stream_ch", &stream_ch); | |
| 103 | TUNABLE_INT("hw.firewire.fwe.tx_speed", &tx_speed); | |
| 104 | TUNABLE_INT("hw.firewire.fwe.rx_queue_len", &rx_queue_len); | |
| 984263bc MD |
105 | |
| 106 | #ifdef DEVICE_POLLING | |
| 984263bc MD |
107 | |
| 108 | static void | |
| 109 | fwe_poll(struct ifnet *ifp, enum poll_cmd cmd, int count) | |
| 110 | { | |
| 111 | struct fwe_softc *fwe; | |
| 112 | struct firewire_comm *fc; | |
| 113 | ||
| 114 | fwe = ((struct fwe_eth_softc *)ifp->if_softc)->fwe; | |
| 115 | fc = fwe->fd.fc; | |
| 9c095379 MD |
116 | switch(cmd) { |
| 117 | case POLL_REGISTER: | |
| 118 | /* disable interrupts */ | |
| 119 | fc->set_intr(fc, 0); | |
| 120 | break; | |
| 121 | case POLL_DEREGISTER: | |
| 984263bc MD |
122 | /* enable interrupts */ |
| 123 | fc->set_intr(fc, 1); | |
| 9c095379 MD |
124 | break; |
| 125 | default: | |
| 126 | fc->poll(fc, (cmd == POLL_AND_CHECK_STATUS)?0:1, count); | |
| 127 | break; | |
| 984263bc | 128 | } |
| 984263bc | 129 | } |
| 9c095379 | 130 | |
| 984263bc | 131 | #endif |
| 9c095379 | 132 | |
| 984263bc MD |
133 | static int |
| 134 | fwe_probe(device_t dev) | |
| 135 | { | |
| 136 | device_t pa; | |
| 137 | ||
| 138 | pa = device_get_parent(dev); | |
| 139 | if(device_get_unit(dev) != device_get_unit(pa)){ | |
| 140 | return(ENXIO); | |
| 141 | } | |
| 142 | ||
| 143 | device_set_desc(dev, "Ethernet over FireWire"); | |
| 144 | return (0); | |
| 145 | } | |
| 146 | ||
| 147 | static int | |
| 148 | fwe_attach(device_t dev) | |
| 149 | { | |
| 150 | struct fwe_softc *fwe; | |
| 151 | struct ifnet *ifp; | |
| 18618b5e | 152 | uint8_t eaddr[ETHER_ADDR_LEN]; |
| 984263bc MD |
153 | struct fw_eui64 *eui; |
| 154 | ||
| 155 | fwe = ((struct fwe_softc *)device_get_softc(dev)); | |
| 984263bc | 156 | |
| 984263bc MD |
157 | /* XXX */ |
| 158 | fwe->stream_ch = stream_ch; | |
| 159 | fwe->dma_ch = -1; | |
| 160 | ||
| 161 | fwe->fd.fc = device_get_ivars(dev); | |
| 78748771 JS |
162 | if (tx_speed < 0) |
| 163 | tx_speed = fwe->fd.fc->speed; | |
| 164 | ||
| 984263bc MD |
165 | fwe->fd.dev = dev; |
| 166 | fwe->fd.post_explore = NULL; | |
| 167 | fwe->eth_softc.fwe = fwe; | |
| 168 | ||
| 169 | fwe->pkt_hdr.mode.stream.tcode = FWTCODE_STREAM; | |
| 170 | fwe->pkt_hdr.mode.stream.sy = 0; | |
| 171 | fwe->pkt_hdr.mode.stream.chtag = fwe->stream_ch; | |
| 172 | ||
| 173 | /* generate fake MAC address: first and last 3bytes from eui64 */ | |
| 174 | #define LOCAL (0x02) | |
| 175 | #define GROUP (0x01) | |
| 984263bc MD |
176 | |
| 177 | eui = &fwe->fd.fc->eui; | |
| 178 | eaddr[0] = (FW_EUI64_BYTE(eui, 0) | LOCAL) & ~GROUP; | |
| 179 | eaddr[1] = FW_EUI64_BYTE(eui, 1); | |
| 180 | eaddr[2] = FW_EUI64_BYTE(eui, 2); | |
| 181 | eaddr[3] = FW_EUI64_BYTE(eui, 5); | |
| 182 | eaddr[4] = FW_EUI64_BYTE(eui, 6); | |
| 183 | eaddr[5] = FW_EUI64_BYTE(eui, 7); | |
| 984263bc MD |
184 | |
| 185 | /* fill the rest and attach interface */ | |
| 186 | ifp = &fwe->fwe_if; | |
| 187 | ifp->if_softc = &fwe->eth_softc; | |
| 188 | ||
| 18618b5e | 189 | if_initname(ifp, device_get_name(dev), device_get_unit(dev)); |
| 984263bc | 190 | ifp->if_init = fwe_init; |
| 984263bc MD |
191 | ifp->if_start = fwe_start; |
| 192 | ifp->if_ioctl = fwe_ioctl; | |
| ba924435 | 193 | ifp->if_capabilities = IFCAP_VLAN_MTU; |
| 9c095379 MD |
194 | #ifdef DEVICE_POLLING |
| 195 | ifp->if_poll = fwe_poll; | |
| 196 | #endif | |
| 984263bc MD |
197 | ifp->if_mtu = ETHERMTU; |
| 198 | ifp->if_flags = (IFF_BROADCAST|IFF_SIMPLEX|IFF_MULTICAST); | |
| efa919fe JS |
199 | ifq_set_maxlen(&ifp->if_snd, TX_MAX_QUEUE); |
| 200 | ifq_set_ready(&ifp->if_snd); | |
| 984263bc | 201 | |
| 78195a76 | 202 | ether_ifattach(ifp, eaddr, NULL); |
| 984263bc MD |
203 | |
| 204 | /* Tell the upper layer(s) we support long frames. */ | |
| 205 | ifp->if_data.ifi_hdrlen = sizeof(struct ether_vlan_header); | |
| 984263bc MD |
206 | |
| 207 | ||
| 78748771 | 208 | FWEDEBUG(ifp, "interface created\n"); |
| 984263bc MD |
209 | return 0; |
| 210 | } | |
| 211 | ||
| 212 | static void | |
| 213 | fwe_stop(struct fwe_softc *fwe) | |
| 214 | { | |
| 215 | struct firewire_comm *fc; | |
| 216 | struct fw_xferq *xferq; | |
| 217 | struct ifnet *ifp = &fwe->fwe_if; | |
| 218 | struct fw_xfer *xfer, *next; | |
| 219 | int i; | |
| 220 | ||
| 221 | fc = fwe->fd.fc; | |
| 222 | ||
| 9c095379 | 223 | ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE); |
| 984263bc MD |
224 | |
| 225 | if (fwe->dma_ch >= 0) { | |
| 226 | xferq = fc->ir[fwe->dma_ch]; | |
| 227 | ||
| 228 | if (xferq->flag & FWXFERQ_RUNNING) | |
| 229 | fc->irx_disable(fc, fwe->dma_ch); | |
| 230 | xferq->flag &= | |
| 231 | ~(FWXFERQ_MODEMASK | FWXFERQ_OPEN | FWXFERQ_STREAM | | |
| 232 | FWXFERQ_EXTBUF | FWXFERQ_HANDLER | FWXFERQ_CHTAGMASK); | |
| 233 | xferq->hand = NULL; | |
| 234 | ||
| 235 | for (i = 0; i < xferq->bnchunk; i ++) | |
| 236 | m_freem(xferq->bulkxfer[i].mbuf); | |
| efda3bd0 | 237 | kfree(xferq->bulkxfer, M_FWE); |
| 984263bc MD |
238 | |
| 239 | for (xfer = STAILQ_FIRST(&fwe->xferlist); xfer != NULL; | |
| 240 | xfer = next) { | |
| 241 | next = STAILQ_NEXT(xfer, link); | |
| 242 | fw_xfer_free(xfer); | |
| 243 | } | |
| 244 | STAILQ_INIT(&fwe->xferlist); | |
| 245 | ||
| 246 | xferq->bulkxfer = NULL; | |
| 247 | fwe->dma_ch = -1; | |
| 248 | } | |
| 984263bc MD |
249 | } |
| 250 | ||
| 251 | static int | |
| 252 | fwe_detach(device_t dev) | |
| 253 | { | |
| 07f992ed | 254 | struct fwe_softc *fwe = device_get_softc(dev); |
| 984263bc | 255 | |
| 78195a76 | 256 | lwkt_serialize_enter(fwe->fwe_if.if_serializer); |
| 984263bc | 257 | fwe_stop(fwe); |
| 78195a76 | 258 | lwkt_serialize_exit(fwe->fwe_if.if_serializer); |
| cdf89432 SZ |
259 | |
| 260 | ether_ifdetach(&fwe->fwe_if); | |
| 984263bc MD |
261 | return 0; |
| 262 | } | |
| 263 | ||
| 264 | static void | |
| 265 | fwe_init(void *arg) | |
| 266 | { | |
| 267 | struct fwe_softc *fwe = ((struct fwe_eth_softc *)arg)->fwe; | |
| 268 | struct firewire_comm *fc; | |
| 269 | struct ifnet *ifp = &fwe->fwe_if; | |
| 270 | struct fw_xferq *xferq; | |
| 271 | struct fw_xfer *xfer; | |
| 272 | struct mbuf *m; | |
| 273 | int i; | |
| 274 | ||
| 78748771 | 275 | FWEDEBUG(ifp, "initializing\n"); |
| 984263bc MD |
276 | |
| 277 | /* XXX keep promiscoud mode */ | |
| 278 | ifp->if_flags |= IFF_PROMISC; | |
| 279 | ||
| 280 | fc = fwe->fd.fc; | |
| 281 | #define START 0 | |
| 282 | if (fwe->dma_ch < 0) { | |
| 984263bc MD |
283 | for (i = START; i < fc->nisodma; i ++) { |
| 284 | xferq = fc->ir[i]; | |
| 285 | if ((xferq->flag & FWXFERQ_OPEN) == 0) | |
| 78748771 | 286 | goto found; |
| 984263bc | 287 | } |
| 18618b5e | 288 | if_printf(ifp, "no free dma channel\n"); |
| 78748771 JS |
289 | return; |
| 290 | found: | |
| 984263bc MD |
291 | fwe->dma_ch = i; |
| 292 | fwe->stream_ch = stream_ch; | |
| 293 | fwe->pkt_hdr.mode.stream.chtag = fwe->stream_ch; | |
| 294 | /* allocate DMA channel and init packet mode */ | |
| 295 | xferq->flag |= FWXFERQ_OPEN | FWXFERQ_EXTBUF | | |
| 296 | FWXFERQ_HANDLER | FWXFERQ_STREAM; | |
| 297 | xferq->flag &= ~0xff; | |
| 298 | xferq->flag |= fwe->stream_ch & 0xff; | |
| 299 | /* register fwe_input handler */ | |
| 300 | xferq->sc = (caddr_t) fwe; | |
| 301 | xferq->hand = fwe_as_input; | |
| 78748771 | 302 | xferq->bnchunk = rx_queue_len; |
| 984263bc MD |
303 | xferq->bnpacket = 1; |
| 304 | xferq->psize = MCLBYTES; | |
| 305 | xferq->queued = 0; | |
| 306 | xferq->buf = NULL; | |
| 77652cad | 307 | xferq->bulkxfer = (struct fw_bulkxfer *) kmalloc( |
| 984263bc MD |
308 | sizeof(struct fw_bulkxfer) * xferq->bnchunk, |
| 309 | M_FWE, M_WAITOK); | |
| 984263bc MD |
310 | STAILQ_INIT(&xferq->stvalid); |
| 311 | STAILQ_INIT(&xferq->stfree); | |
| 312 | STAILQ_INIT(&xferq->stdma); | |
| 313 | xferq->stproc = NULL; | |
| 314 | for (i = 0; i < xferq->bnchunk; i ++) { | |
| ba924435 | 315 | m = m_getcl(MB_WAIT, MT_DATA, M_PKTHDR); |
| 984263bc MD |
316 | xferq->bulkxfer[i].mbuf = m; |
| 317 | if (m != NULL) { | |
| 318 | m->m_len = m->m_pkthdr.len = m->m_ext.ext_size; | |
| 319 | STAILQ_INSERT_TAIL(&xferq->stfree, | |
| 320 | &xferq->bulkxfer[i], link); | |
| 18618b5e JS |
321 | } else { |
| 322 | if_printf(ifp, "fwe_init: m_getcl failed\n"); | |
| 323 | } | |
| 984263bc MD |
324 | } |
| 325 | STAILQ_INIT(&fwe->xferlist); | |
| 326 | for (i = 0; i < TX_MAX_QUEUE; i++) { | |
| 327 | xfer = fw_xfer_alloc(M_FWE); | |
| 328 | if (xfer == NULL) | |
| 329 | break; | |
| 78748771 | 330 | xfer->send.spd = tx_speed; |
| 984263bc MD |
331 | xfer->fc = fwe->fd.fc; |
| 332 | xfer->retry_req = fw_asybusy; | |
| 333 | xfer->sc = (caddr_t)fwe; | |
| 334 | xfer->act.hand = fwe_output_callback; | |
| 335 | STAILQ_INSERT_TAIL(&fwe->xferlist, xfer, link); | |
| 336 | } | |
| 337 | } else | |
| 338 | xferq = fc->ir[fwe->dma_ch]; | |
| 339 | ||
| 1736eecd SZ |
340 | #ifdef DEVICE_POLLING |
| 341 | /* Disable interrupt, if polling(4) is enabled */ | |
| 342 | if (ifp->if_flags & IFF_POLLING) | |
| 343 | fc->set_intr(fc, 0); | |
| 344 | else | |
| 345 | #endif | |
| 346 | fc->set_intr(fc, 1); | |
| 984263bc MD |
347 | |
| 348 | /* start dma */ | |
| 349 | if ((xferq->flag & FWXFERQ_RUNNING) == 0) | |
| 350 | fc->irx_enable(fc, fwe->dma_ch); | |
| 351 | ||
| 352 | ifp->if_flags |= IFF_RUNNING; | |
| 353 | ifp->if_flags &= ~IFF_OACTIVE; | |
| 984263bc MD |
354 | } |
| 355 | ||
| 356 | ||
| 357 | static int | |
| bd4539cc | 358 | fwe_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data, struct ucred *cr) |
| 984263bc MD |
359 | { |
| 360 | struct fwe_softc *fwe = ((struct fwe_eth_softc *)ifp->if_softc)->fwe; | |
| 361 | struct ifstat *ifs = NULL; | |
| 07f992ed JS |
362 | int error = 0, len; |
| 363 | ||
| 984263bc | 364 | switch (cmd) { |
| ba924435 | 365 | case SIOCSIFFLAGS: |
| ba924435 JS |
366 | if (ifp->if_flags & IFF_UP) { |
| 367 | if (!(ifp->if_flags & IFF_RUNNING)) | |
| 368 | fwe_init(&fwe->eth_softc); | |
| 369 | } else { | |
| 370 | if (ifp->if_flags & IFF_RUNNING) | |
| 371 | fwe_stop(fwe); | |
| 372 | } | |
| 373 | /* XXX keep promiscoud mode */ | |
| 374 | ifp->if_flags |= IFF_PROMISC; | |
| ba924435 JS |
375 | break; |
| 376 | case SIOCADDMULTI: | |
| 377 | case SIOCDELMULTI: | |
| 378 | break; | |
| 379 | ||
| 380 | case SIOCGIFSTATUS: | |
| ba924435 JS |
381 | ifs = (struct ifstat *)data; |
| 382 | len = strlen(ifs->ascii); | |
| 383 | if (len < sizeof(ifs->ascii)) | |
| f8c7a42d | 384 | ksnprintf(ifs->ascii + len, |
| ba924435 JS |
385 | sizeof(ifs->ascii) - len, |
| 386 | "\tch %d dma %d\n", | |
| 387 | fwe->stream_ch, fwe->dma_ch); | |
| ba924435 JS |
388 | break; |
| 389 | default: | |
| ba924435 | 390 | error = ether_ioctl(ifp, cmd, data); |
| 07f992ed | 391 | break; |
| 984263bc | 392 | } |
| 07f992ed | 393 | return (error); |
| 984263bc MD |
394 | } |
| 395 | ||
| 396 | static void | |
| 397 | fwe_output_callback(struct fw_xfer *xfer) | |
| 398 | { | |
| 399 | struct fwe_softc *fwe; | |
| 400 | struct ifnet *ifp; | |
| 984263bc MD |
401 | |
| 402 | fwe = (struct fwe_softc *)xfer->sc; | |
| 403 | ifp = &fwe->fwe_if; | |
| 78195a76 | 404 | lwkt_serialize_enter(ifp->if_serializer); |
| 984263bc | 405 | /* XXX error check */ |
| 78748771 | 406 | FWEDEBUG(ifp, "resp = %d\n", xfer->resp); |
| 984263bc MD |
407 | if (xfer->resp != 0) |
| 408 | ifp->if_oerrors ++; | |
| 409 | ||
| 410 | m_freem(xfer->mbuf); | |
| 984263bc MD |
411 | fw_xfer_unload(xfer); |
| 412 | ||
| 984263bc | 413 | STAILQ_INSERT_TAIL(&fwe->xferlist, xfer, link); |
| 07f992ed | 414 | |
| 984263bc | 415 | /* for queue full */ |
| efa919fe | 416 | if (!ifq_is_empty(&ifp->if_snd)) |
| 9db4b353 | 417 | if_devstart(ifp); |
| 78195a76 | 418 | lwkt_serialize_exit(ifp->if_serializer); |
| 984263bc MD |
419 | } |
| 420 | ||
| 421 | static void | |
| 422 | fwe_start(struct ifnet *ifp) | |
| 423 | { | |
| 424 | struct fwe_softc *fwe = ((struct fwe_eth_softc *)ifp->if_softc)->fwe; | |
| 984263bc | 425 | |
| 78748771 | 426 | FWEDEBUG(ifp, "starting\n"); |
| 984263bc MD |
427 | |
| 428 | if (fwe->dma_ch < 0) { | |
| 78748771 | 429 | FWEDEBUG(ifp, "not ready\n"); |
| 984263bc | 430 | |
| efa919fe | 431 | ifq_purge(&ifp->if_snd); |
| 07f992ed JS |
432 | } else { |
| 433 | ifp->if_flags |= IFF_OACTIVE; | |
| 984263bc | 434 | |
| 07f992ed JS |
435 | if (!ifq_is_empty(&ifp->if_snd)) |
| 436 | fwe_as_output(fwe, ifp); | |
| 984263bc | 437 | |
| 07f992ed JS |
438 | ifp->if_flags &= ~IFF_OACTIVE; |
| 439 | } | |
| 984263bc MD |
440 | } |
| 441 | ||
| 442 | #define HDR_LEN 4 | |
| 443 | #ifndef ETHER_ALIGN | |
| 444 | #define ETHER_ALIGN 2 | |
| 445 | #endif | |
| 446 | /* Async. stream output */ | |
| 447 | static void | |
| 448 | fwe_as_output(struct fwe_softc *fwe, struct ifnet *ifp) | |
| 449 | { | |
| 450 | struct mbuf *m; | |
| 451 | struct fw_xfer *xfer; | |
| 452 | struct fw_xferq *xferq; | |
| 453 | struct fw_pkt *fp; | |
| 454 | int i = 0; | |
| 455 | ||
| 456 | xfer = NULL; | |
| 457 | xferq = fwe->fd.fc->atq; | |
| 458 | while (xferq->queued < xferq->maxq - 1) { | |
| 459 | xfer = STAILQ_FIRST(&fwe->xferlist); | |
| 460 | if (xfer == NULL) { | |
| 18618b5e | 461 | if_printf(ifp, "lack of xfer\n"); |
| 984263bc MD |
462 | return; |
| 463 | } | |
| d2c71fa0 | 464 | m = ifq_dequeue(&ifp->if_snd, NULL); |
| 984263bc MD |
465 | if (m == NULL) |
| 466 | break; | |
| 467 | STAILQ_REMOVE_HEAD(&fwe->xferlist, link); | |
| 90dec0df | 468 | BPF_MTAP(ifp, m); |
| 984263bc MD |
469 | |
| 470 | /* keep ip packet alignment for alpha */ | |
| 74f1caca | 471 | M_PREPEND(m, ETHER_ALIGN, MB_DONTWAIT); |
| 78748771 JS |
472 | fp = &xfer->send.hdr; |
| 473 | *(u_int32_t *)&xfer->send.hdr = *(int32_t *)&fwe->pkt_hdr; | |
| 984263bc | 474 | fp->mode.stream.len = m->m_pkthdr.len; |
| 984263bc | 475 | xfer->mbuf = m; |
| 78748771 | 476 | xfer->send.pay_len = m->m_pkthdr.len; |
| 984263bc MD |
477 | |
| 478 | if (fw_asyreq(fwe->fd.fc, -1, xfer) != 0) { | |
| 479 | /* error */ | |
| 480 | ifp->if_oerrors ++; | |
| 481 | /* XXX set error code */ | |
| 482 | fwe_output_callback(xfer); | |
| 483 | } else { | |
| 484 | ifp->if_opackets ++; | |
| 485 | i++; | |
| 486 | } | |
| 487 | } | |
| 488 | #if 0 | |
| 489 | if (i > 1) | |
| 18618b5e | 490 | if_printf(ifp, "%d queued\n", i); |
| 984263bc MD |
491 | #endif |
| 492 | if (i > 0) | |
| 493 | xferq->start(fwe->fd.fc); | |
| 494 | } | |
| 495 | ||
| 496 | /* Async. stream output */ | |
| 497 | static void | |
| 498 | fwe_as_input(struct fw_xferq *xferq) | |
| 499 | { | |
| 500 | struct mbuf *m, *m0; | |
| 501 | struct ifnet *ifp; | |
| 502 | struct fwe_softc *fwe; | |
| 503 | struct fw_bulkxfer *sxfer; | |
| 504 | struct fw_pkt *fp; | |
| 505 | u_char *c; | |
| 984263bc MD |
506 | |
| 507 | fwe = (struct fwe_softc *)xferq->sc; | |
| 508 | ifp = &fwe->fwe_if; | |
| 78195a76 | 509 | lwkt_serialize_enter(ifp->if_serializer); |
| 984263bc MD |
510 | while ((sxfer = STAILQ_FIRST(&xferq->stvalid)) != NULL) { |
| 511 | STAILQ_REMOVE_HEAD(&xferq->stvalid, link); | |
| 984263bc | 512 | fp = mtod(sxfer->mbuf, struct fw_pkt *); |
| 984263bc MD |
513 | if (fwe->fd.fc->irx_post != NULL) |
| 514 | fwe->fd.fc->irx_post(fwe->fd.fc, fp->mode.ld); | |
| 515 | m = sxfer->mbuf; | |
| 516 | ||
| 78748771 | 517 | /* insert new rbuf */ |
| 74f1caca | 518 | sxfer->mbuf = m0 = m_getcl(MB_DONTWAIT, MT_DATA, M_PKTHDR); |
| 984263bc MD |
519 | if (m0 != NULL) { |
| 520 | m0->m_len = m0->m_pkthdr.len = m0->m_ext.ext_size; | |
| 521 | STAILQ_INSERT_TAIL(&xferq->stfree, sxfer, link); | |
| 18618b5e JS |
522 | } else { |
| 523 | if_printf(ifp, "fwe_as_input: m_getcl failed\n"); | |
| 524 | } | |
| 984263bc | 525 | |
| 78748771 JS |
526 | if (sxfer->resp != 0 || fp->mode.stream.len < |
| 527 | ETHER_ALIGN + sizeof(struct ether_header)) { | |
| 528 | m_freem(m); | |
| 529 | ifp->if_ierrors ++; | |
| 530 | continue; | |
| 531 | } | |
| 532 | ||
| 984263bc MD |
533 | m->m_data += HDR_LEN + ETHER_ALIGN; |
| 534 | c = mtod(m, char *); | |
| 984263bc MD |
535 | m->m_len = m->m_pkthdr.len = |
| 536 | fp->mode.stream.len - ETHER_ALIGN; | |
| 537 | m->m_pkthdr.rcvif = ifp; | |
| 538 | #if 0 | |
| 78748771 | 539 | FWEDEBUG(ifp, "%02x %02x %02x %02x %02x %02x\n" |
| 984263bc MD |
540 | "%02x %02x %02x %02x %02x %02x\n" |
| 541 | "%02x %02x %02x %02x\n" | |
| 542 | "%02x %02x %02x %02x\n" | |
| 543 | "%02x %02x %02x %02x\n" | |
| 544 | "%02x %02x %02x %02x\n", | |
| 545 | c[0], c[1], c[2], c[3], c[4], c[5], | |
| 546 | c[6], c[7], c[8], c[9], c[10], c[11], | |
| 547 | c[12], c[13], c[14], c[15], | |
| 548 | c[16], c[17], c[18], c[19], | |
| 549 | c[20], c[21], c[22], c[23], | |
| 550 | c[20], c[21], c[22], c[23] | |
| 551 | ); | |
| 552 | #endif | |
| 78195a76 | 553 | ifp->if_input(ifp, m); |
| 984263bc MD |
554 | ifp->if_ipackets ++; |
| 555 | } | |
| 556 | if (STAILQ_FIRST(&xferq->stfree) != NULL) | |
| 557 | fwe->fd.fc->irx_enable(fwe->fd.fc, fwe->dma_ch); | |
| 78195a76 | 558 | lwkt_serialize_exit(ifp->if_serializer); |
| 984263bc MD |
559 | } |
| 560 | ||
| 561 | ||
| 562 | static devclass_t fwe_devclass; | |
| 563 | ||
| 39b5d600 MD |
564 | /* |
| 565 | * Because fwe is a static device that always exists under any attached | |
| 566 | * firewire device, and not scanned by the firewire device, we need an | |
| 567 | * identify function to install the device. | |
| 568 | */ | |
| 984263bc MD |
569 | static device_method_t fwe_methods[] = { |
| 570 | /* device interface */ | |
| 39b5d600 | 571 | DEVMETHOD(device_identify, bus_generic_identify_sameunit), |
| 984263bc MD |
572 | DEVMETHOD(device_probe, fwe_probe), |
| 573 | DEVMETHOD(device_attach, fwe_attach), | |
| 574 | DEVMETHOD(device_detach, fwe_detach), | |
| 575 | { 0, 0 } | |
| 576 | }; | |
| 577 | ||
| 578 | static driver_t fwe_driver = { | |
| 78748771 | 579 | "fwe", |
| 984263bc MD |
580 | fwe_methods, |
| 581 | sizeof(struct fwe_softc), | |
| 582 | }; | |
| 583 | ||
| 584 | ||
| 78748771 JS |
585 | DECLARE_DUMMY_MODULE(fwe); |
| 586 | DRIVER_MODULE(fwe, firewire, fwe_driver, fwe_devclass, 0, 0); | |
| 587 | MODULE_VERSION(fwe, 1); | |
| 588 | MODULE_DEPEND(fwe, firewire, 1, 1, 1); |