1 | /* $NetBSD: if_url.c,v 1.53 2016/07/07 06:55:42 msaitoh Exp $ */ |
2 | |
3 | /* |
4 | * Copyright (c) 2001, 2002 |
5 | * Shingo WATANABE <nabe@nabechan.org>. All rights reserved. |
6 | * |
7 | * Redistribution and use in source and binary forms, with or without |
8 | * modification, are permitted provided that the following conditions |
9 | * are met: |
10 | * 1. Redistributions of source code must retain the above copyright |
11 | * notice, this list of conditions and the following disclaimer. |
12 | * 2. Redistributions in binary form must reproduce the above copyright |
13 | * notice, this list of conditions and the following disclaimer in the |
14 | * documentation and/or other materials provided with the distribution. |
15 | * 3. Neither the name of the author nor the names of any co-contributors |
16 | * may be used to endorse or promote products derived from this software |
17 | * without specific prior written permission. |
18 | * |
19 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND |
20 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
21 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
22 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE |
23 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
24 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
25 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
26 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
27 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
28 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
29 | * SUCH DAMAGE. |
30 | * |
31 | */ |
32 | |
33 | /* |
34 | * The RTL8150L(Realtek USB to fast ethernet controller) spec can be found at |
35 | * ftp://ftp.realtek.com.tw/lancard/data_sheet/8150/8150v14.pdf |
36 | * ftp://152.104.125.40/lancard/data_sheet/8150/8150v14.pdf |
37 | */ |
38 | |
39 | /* |
40 | * TODO: |
41 | * Interrupt Endpoint support |
42 | * External PHYs |
43 | * powerhook() support? |
44 | */ |
45 | |
46 | #include <sys/cdefs.h> |
47 | __KERNEL_RCSID(0, "$NetBSD: if_url.c,v 1.53 2016/07/07 06:55:42 msaitoh Exp $" ); |
48 | |
49 | #ifdef _KERNEL_OPT |
50 | #include "opt_inet.h" |
51 | #endif |
52 | |
53 | #include <sys/param.h> |
54 | #include <sys/systm.h> |
55 | #include <sys/rwlock.h> |
56 | #include <sys/mbuf.h> |
57 | #include <sys/kernel.h> |
58 | #include <sys/socket.h> |
59 | |
60 | #include <sys/device.h> |
61 | #include <sys/rndsource.h> |
62 | |
63 | #include <net/if.h> |
64 | #include <net/if_arp.h> |
65 | #include <net/if_dl.h> |
66 | #include <net/if_media.h> |
67 | |
68 | #include <net/bpf.h> |
69 | |
70 | #include <net/if_ether.h> |
71 | #ifdef INET |
72 | #include <netinet/in.h> |
73 | #include <netinet/if_inarp.h> |
74 | #endif |
75 | |
76 | #include <dev/mii/mii.h> |
77 | #include <dev/mii/miivar.h> |
78 | #include <dev/mii/urlphyreg.h> |
79 | |
80 | #include <dev/usb/usb.h> |
81 | #include <dev/usb/usbdi.h> |
82 | #include <dev/usb/usbdi_util.h> |
83 | #include <dev/usb/usbdevs.h> |
84 | |
85 | #include <dev/usb/if_urlreg.h> |
86 | |
87 | |
88 | /* Function declarations */ |
89 | int url_match(device_t, cfdata_t, void *); |
90 | void url_attach(device_t, device_t, void *); |
91 | int url_detach(device_t, int); |
92 | int url_activate(device_t, enum devact); |
93 | extern struct cfdriver url_cd; |
94 | CFATTACH_DECL_NEW(url, sizeof(struct url_softc), url_match, url_attach, |
95 | url_detach, url_activate); |
96 | |
97 | Static int url_openpipes(struct url_softc *); |
98 | Static int url_rx_list_init(struct url_softc *); |
99 | Static int url_tx_list_init(struct url_softc *); |
100 | Static int url_newbuf(struct url_softc *, struct url_chain *, struct mbuf *); |
101 | Static void url_start(struct ifnet *); |
102 | Static int url_send(struct url_softc *, struct mbuf *, int); |
103 | Static void url_txeof(struct usbd_xfer *, void *, usbd_status); |
104 | Static void url_rxeof(struct usbd_xfer *, void *, usbd_status); |
105 | Static void url_tick(void *); |
106 | Static void url_tick_task(void *); |
107 | Static int url_ioctl(struct ifnet *, u_long, void *); |
108 | Static void url_stop_task(struct url_softc *); |
109 | Static void url_stop(struct ifnet *, int); |
110 | Static void url_watchdog(struct ifnet *); |
111 | Static int url_ifmedia_change(struct ifnet *); |
112 | Static void url_ifmedia_status(struct ifnet *, struct ifmediareq *); |
113 | Static void url_lock_mii(struct url_softc *); |
114 | Static void url_unlock_mii(struct url_softc *); |
115 | Static int url_int_miibus_readreg(device_t, int, int); |
116 | Static void url_int_miibus_writereg(device_t, int, int, int); |
117 | Static void url_miibus_statchg(struct ifnet *); |
118 | Static int url_init(struct ifnet *); |
119 | Static void url_setmulti(struct url_softc *); |
120 | Static void url_reset(struct url_softc *); |
121 | |
122 | Static int url_csr_read_1(struct url_softc *, int); |
123 | Static int url_csr_read_2(struct url_softc *, int); |
124 | Static int url_csr_write_1(struct url_softc *, int, int); |
125 | Static int url_csr_write_2(struct url_softc *, int, int); |
126 | Static int url_csr_write_4(struct url_softc *, int, int); |
127 | Static int url_mem(struct url_softc *, int, int, void *, int); |
128 | |
129 | /* Macros */ |
130 | #ifdef URL_DEBUG |
131 | #define DPRINTF(x) if (urldebug) printf x |
132 | #define DPRINTFN(n,x) if (urldebug >= (n)) printf x |
133 | int urldebug = 0; |
134 | #else |
135 | #define DPRINTF(x) |
136 | #define DPRINTFN(n,x) |
137 | #endif |
138 | |
139 | #define URL_SETBIT(sc, reg, x) \ |
140 | url_csr_write_1(sc, reg, url_csr_read_1(sc, reg) | (x)) |
141 | |
142 | #define URL_SETBIT2(sc, reg, x) \ |
143 | url_csr_write_2(sc, reg, url_csr_read_2(sc, reg) | (x)) |
144 | |
145 | #define URL_CLRBIT(sc, reg, x) \ |
146 | url_csr_write_1(sc, reg, url_csr_read_1(sc, reg) & ~(x)) |
147 | |
148 | #define URL_CLRBIT2(sc, reg, x) \ |
149 | url_csr_write_2(sc, reg, url_csr_read_2(sc, reg) & ~(x)) |
150 | |
151 | static const struct url_type { |
152 | struct usb_devno url_dev; |
153 | uint16_t url_flags; |
154 | #define URL_EXT_PHY 0x0001 |
155 | } url_devs [] = { |
156 | /* MELCO LUA-KTX */ |
157 | {{ USB_VENDOR_MELCO, USB_PRODUCT_MELCO_LUAKTX }, 0}, |
158 | /* Realtek RTL8150L Generic (GREEN HOUSE USBKR100) */ |
159 | {{ USB_VENDOR_REALTEK, USB_PRODUCT_REALTEK_RTL8150L}, 0}, |
160 | /* Longshine LCS-8138TX */ |
161 | {{ USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_LCS8138TX}, 0}, |
162 | /* Micronet SP128AR */ |
163 | {{ USB_VENDOR_MICRONET, USB_PRODUCT_MICRONET_SP128AR}, 0}, |
164 | /* OQO model 01 */ |
165 | {{ USB_VENDOR_OQO, USB_PRODUCT_OQO_ETHER01}, 0}, |
166 | }; |
167 | #define url_lookup(v, p) ((const struct url_type *)usb_lookup(url_devs, v, p)) |
168 | |
169 | |
170 | /* Probe */ |
171 | int |
172 | url_match(device_t parent, cfdata_t match, void *aux) |
173 | { |
174 | struct usb_attach_arg *uaa = aux; |
175 | |
176 | return url_lookup(uaa->uaa_vendor, uaa->uaa_product) != NULL ? |
177 | UMATCH_VENDOR_PRODUCT : UMATCH_NONE; |
178 | } |
179 | /* Attach */ |
180 | void |
181 | url_attach(device_t parent, device_t self, void *aux) |
182 | { |
183 | struct url_softc *sc = device_private(self); |
184 | struct usb_attach_arg *uaa = aux; |
185 | struct usbd_device *dev = uaa->uaa_device; |
186 | struct usbd_interface *iface; |
187 | usbd_status err; |
188 | usb_interface_descriptor_t *id; |
189 | usb_endpoint_descriptor_t *ed; |
190 | char *devinfop; |
191 | struct ifnet *ifp; |
192 | struct mii_data *mii; |
193 | u_char eaddr[ETHER_ADDR_LEN]; |
194 | int i, s; |
195 | |
196 | sc->sc_dev = self; |
197 | |
198 | aprint_naive("\n" ); |
199 | aprint_normal("\n" ); |
200 | |
201 | devinfop = usbd_devinfo_alloc(dev, 0); |
202 | aprint_normal_dev(self, "%s\n" , devinfop); |
203 | usbd_devinfo_free(devinfop); |
204 | |
205 | /* Move the device into the configured state. */ |
206 | err = usbd_set_config_no(dev, URL_CONFIG_NO, 1); |
207 | if (err) { |
208 | aprint_error_dev(self, "failed to set configuration" |
209 | ", err=%s\n" , usbd_errstr(err)); |
210 | goto bad; |
211 | } |
212 | |
213 | usb_init_task(&sc->sc_tick_task, url_tick_task, sc, 0); |
214 | rw_init(&sc->sc_mii_rwlock); |
215 | usb_init_task(&sc->sc_stop_task, (void (*)(void *))url_stop_task, sc, 0); |
216 | |
217 | /* get control interface */ |
218 | err = usbd_device2interface_handle(dev, URL_IFACE_INDEX, &iface); |
219 | if (err) { |
220 | aprint_error_dev(self, "failed to get interface, err=%s\n" , |
221 | usbd_errstr(err)); |
222 | goto bad; |
223 | } |
224 | |
225 | sc->sc_udev = dev; |
226 | sc->sc_ctl_iface = iface; |
227 | sc->sc_flags = url_lookup(uaa->uaa_vendor, uaa->uaa_product)->url_flags; |
228 | |
229 | /* get interface descriptor */ |
230 | id = usbd_get_interface_descriptor(sc->sc_ctl_iface); |
231 | |
232 | /* find endpoints */ |
233 | sc->sc_bulkin_no = sc->sc_bulkout_no = sc->sc_intrin_no = -1; |
234 | for (i = 0; i < id->bNumEndpoints; i++) { |
235 | ed = usbd_interface2endpoint_descriptor(sc->sc_ctl_iface, i); |
236 | if (ed == NULL) { |
237 | aprint_error_dev(self, |
238 | "couldn't get endpoint %d\n" , i); |
239 | goto bad; |
240 | } |
241 | if ((ed->bmAttributes & UE_XFERTYPE) == UE_BULK && |
242 | UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN) |
243 | sc->sc_bulkin_no = ed->bEndpointAddress; /* RX */ |
244 | else if ((ed->bmAttributes & UE_XFERTYPE) == UE_BULK && |
245 | UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT) |
246 | sc->sc_bulkout_no = ed->bEndpointAddress; /* TX */ |
247 | else if ((ed->bmAttributes & UE_XFERTYPE) == UE_INTERRUPT && |
248 | UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN) |
249 | sc->sc_intrin_no = ed->bEndpointAddress; /* Status */ |
250 | } |
251 | |
252 | if (sc->sc_bulkin_no == -1 || sc->sc_bulkout_no == -1 || |
253 | sc->sc_intrin_no == -1) { |
254 | aprint_error_dev(self, "missing endpoint\n" ); |
255 | goto bad; |
256 | } |
257 | |
258 | s = splnet(); |
259 | |
260 | /* reset the adapter */ |
261 | url_reset(sc); |
262 | |
263 | /* Get Ethernet Address */ |
264 | err = url_mem(sc, URL_CMD_READMEM, URL_IDR0, (void *)eaddr, |
265 | ETHER_ADDR_LEN); |
266 | if (err) { |
267 | aprint_error_dev(self, "read MAC address failed\n" ); |
268 | splx(s); |
269 | goto bad; |
270 | } |
271 | |
272 | /* Print Ethernet Address */ |
273 | aprint_normal_dev(self, "Ethernet address %s\n" , ether_sprintf(eaddr)); |
274 | |
275 | /* initialize interface information */ |
276 | ifp = GET_IFP(sc); |
277 | ifp->if_softc = sc; |
278 | ifp->if_mtu = ETHERMTU; |
279 | strncpy(ifp->if_xname, device_xname(self), IFNAMSIZ); |
280 | ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; |
281 | ifp->if_start = url_start; |
282 | ifp->if_ioctl = url_ioctl; |
283 | ifp->if_watchdog = url_watchdog; |
284 | ifp->if_init = url_init; |
285 | ifp->if_stop = url_stop; |
286 | |
287 | IFQ_SET_READY(&ifp->if_snd); |
288 | |
289 | /* |
290 | * Do ifmedia setup. |
291 | */ |
292 | mii = &sc->sc_mii; |
293 | mii->mii_ifp = ifp; |
294 | mii->mii_readreg = url_int_miibus_readreg; |
295 | mii->mii_writereg = url_int_miibus_writereg; |
296 | #if 0 |
297 | if (sc->sc_flags & URL_EXT_PHY) { |
298 | mii->mii_readreg = url_ext_miibus_readreg; |
299 | mii->mii_writereg = url_ext_miibus_writereg; |
300 | } |
301 | #endif |
302 | mii->mii_statchg = url_miibus_statchg; |
303 | mii->mii_flags = MIIF_AUTOTSLEEP; |
304 | sc->sc_ec.ec_mii = mii; |
305 | ifmedia_init(&mii->mii_media, 0, |
306 | url_ifmedia_change, url_ifmedia_status); |
307 | mii_attach(self, mii, 0xffffffff, MII_PHY_ANY, MII_OFFSET_ANY, 0); |
308 | if (LIST_FIRST(&mii->mii_phys) == NULL) { |
309 | ifmedia_add(&mii->mii_media, IFM_ETHER | IFM_NONE, 0, NULL); |
310 | ifmedia_set(&mii->mii_media, IFM_ETHER | IFM_NONE); |
311 | } else |
312 | ifmedia_set(&mii->mii_media, IFM_ETHER | IFM_AUTO); |
313 | |
314 | /* attach the interface */ |
315 | if_attach(ifp); |
316 | ether_ifattach(ifp, eaddr); |
317 | |
318 | rnd_attach_source(&sc->rnd_source, device_xname(self), |
319 | RND_TYPE_NET, RND_FLAG_DEFAULT); |
320 | |
321 | callout_init(&sc->sc_stat_ch, 0); |
322 | sc->sc_attached = 1; |
323 | splx(s); |
324 | |
325 | usbd_add_drv_event(USB_EVENT_DRIVER_ATTACH, dev, sc->sc_dev); |
326 | |
327 | return; |
328 | |
329 | bad: |
330 | sc->sc_dying = 1; |
331 | return; |
332 | } |
333 | |
334 | /* detach */ |
335 | int |
336 | url_detach(device_t self, int flags) |
337 | { |
338 | struct url_softc *sc = device_private(self); |
339 | struct ifnet *ifp = GET_IFP(sc); |
340 | int s; |
341 | |
342 | DPRINTF(("%s: %s: enter\n" , device_xname(sc->sc_dev), __func__)); |
343 | |
344 | /* Detached before attached finished */ |
345 | if (!sc->sc_attached) |
346 | return 0; |
347 | |
348 | callout_stop(&sc->sc_stat_ch); |
349 | |
350 | /* Remove any pending tasks */ |
351 | usb_rem_task(sc->sc_udev, &sc->sc_tick_task); |
352 | usb_rem_task(sc->sc_udev, &sc->sc_stop_task); |
353 | |
354 | s = splusb(); |
355 | |
356 | if (--sc->sc_refcnt >= 0) { |
357 | /* Wait for processes to go away */ |
358 | usb_detach_waitold(sc->sc_dev); |
359 | } |
360 | |
361 | if (ifp->if_flags & IFF_RUNNING) |
362 | url_stop(GET_IFP(sc), 1); |
363 | |
364 | rnd_detach_source(&sc->rnd_source); |
365 | mii_detach(&sc->sc_mii, MII_PHY_ANY, MII_OFFSET_ANY); |
366 | ifmedia_delete_instance(&sc->sc_mii.mii_media, IFM_INST_ANY); |
367 | ether_ifdetach(ifp); |
368 | if_detach(ifp); |
369 | |
370 | #ifdef DIAGNOSTIC |
371 | if (sc->sc_pipe_tx != NULL) |
372 | aprint_debug_dev(self, "detach has active tx endpoint.\n" ); |
373 | if (sc->sc_pipe_rx != NULL) |
374 | aprint_debug_dev(self, "detach has active rx endpoint.\n" ); |
375 | if (sc->sc_pipe_intr != NULL) |
376 | aprint_debug_dev(self, "detach has active intr endpoint.\n" ); |
377 | #endif |
378 | |
379 | sc->sc_attached = 0; |
380 | |
381 | splx(s); |
382 | |
383 | rw_destroy(&sc->sc_mii_rwlock); |
384 | usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, sc->sc_udev, sc->sc_dev); |
385 | |
386 | return 0; |
387 | } |
388 | |
389 | /* read/write memory */ |
390 | Static int |
391 | url_mem(struct url_softc *sc, int cmd, int offset, void *buf, int len) |
392 | { |
393 | usb_device_request_t req; |
394 | usbd_status err; |
395 | |
396 | if (sc == NULL) |
397 | return 0; |
398 | |
399 | DPRINTFN(0x200, |
400 | ("%s: %s: enter\n" , device_xname(sc->sc_dev), __func__)); |
401 | |
402 | if (sc->sc_dying) |
403 | return 0; |
404 | |
405 | if (cmd == URL_CMD_READMEM) |
406 | req.bmRequestType = UT_READ_VENDOR_DEVICE; |
407 | else |
408 | req.bmRequestType = UT_WRITE_VENDOR_DEVICE; |
409 | req.bRequest = URL_REQ_MEM; |
410 | USETW(req.wValue, offset); |
411 | USETW(req.wIndex, 0x0000); |
412 | USETW(req.wLength, len); |
413 | |
414 | sc->sc_refcnt++; |
415 | err = usbd_do_request(sc->sc_udev, &req, buf); |
416 | if (--sc->sc_refcnt < 0) |
417 | usb_detach_wakeupold(sc->sc_dev); |
418 | if (err) { |
419 | DPRINTF(("%s: url_mem(): %s failed. off=%04x, err=%d\n" , |
420 | device_xname(sc->sc_dev), |
421 | cmd == URL_CMD_READMEM ? "read" : "write" , |
422 | offset, err)); |
423 | } |
424 | |
425 | return err; |
426 | } |
427 | |
428 | /* read 1byte from register */ |
429 | Static int |
430 | url_csr_read_1(struct url_softc *sc, int reg) |
431 | { |
432 | uint8_t val = 0; |
433 | |
434 | DPRINTFN(0x100, |
435 | ("%s: %s: enter\n" , device_xname(sc->sc_dev), __func__)); |
436 | |
437 | if (sc->sc_dying) |
438 | return 0; |
439 | |
440 | return url_mem(sc, URL_CMD_READMEM, reg, &val, 1) ? 0 : val; |
441 | } |
442 | |
443 | /* read 2bytes from register */ |
444 | Static int |
445 | url_csr_read_2(struct url_softc *sc, int reg) |
446 | { |
447 | uWord val; |
448 | |
449 | DPRINTFN(0x100, |
450 | ("%s: %s: enter\n" , device_xname(sc->sc_dev), __func__)); |
451 | |
452 | if (sc->sc_dying) |
453 | return 0; |
454 | |
455 | USETW(val, 0); |
456 | return url_mem(sc, URL_CMD_READMEM, reg, &val, 2) ? 0 : UGETW(val); |
457 | } |
458 | |
459 | /* write 1byte to register */ |
460 | Static int |
461 | url_csr_write_1(struct url_softc *sc, int reg, int aval) |
462 | { |
463 | uint8_t val = aval; |
464 | |
465 | DPRINTFN(0x100, |
466 | ("%s: %s: enter\n" , device_xname(sc->sc_dev), __func__)); |
467 | |
468 | if (sc->sc_dying) |
469 | return 0; |
470 | |
471 | return url_mem(sc, URL_CMD_WRITEMEM, reg, &val, 1) ? -1 : 0; |
472 | } |
473 | |
474 | /* write 2bytes to register */ |
475 | Static int |
476 | url_csr_write_2(struct url_softc *sc, int reg, int aval) |
477 | { |
478 | uWord val; |
479 | |
480 | DPRINTFN(0x100, |
481 | ("%s: %s: enter\n" , device_xname(sc->sc_dev), __func__)); |
482 | |
483 | USETW(val, aval); |
484 | |
485 | if (sc->sc_dying) |
486 | return 0; |
487 | |
488 | return url_mem(sc, URL_CMD_WRITEMEM, reg, &val, 2) ? -1 : 0; |
489 | } |
490 | |
491 | /* write 4bytes to register */ |
492 | Static int |
493 | url_csr_write_4(struct url_softc *sc, int reg, int aval) |
494 | { |
495 | uDWord val; |
496 | |
497 | DPRINTFN(0x100, |
498 | ("%s: %s: enter\n" , device_xname(sc->sc_dev), __func__)); |
499 | |
500 | USETDW(val, aval); |
501 | |
502 | if (sc->sc_dying) |
503 | return 0; |
504 | |
505 | return url_mem(sc, URL_CMD_WRITEMEM, reg, &val, 4) ? -1 : 0; |
506 | } |
507 | |
508 | Static int |
509 | url_init(struct ifnet *ifp) |
510 | { |
511 | struct url_softc *sc = ifp->if_softc; |
512 | struct mii_data *mii = GET_MII(sc); |
513 | const u_char *eaddr; |
514 | int i, rc, s; |
515 | |
516 | DPRINTF(("%s: %s: enter\n" , device_xname(sc->sc_dev), __func__)); |
517 | |
518 | if (sc->sc_dying) |
519 | return EIO; |
520 | |
521 | s = splnet(); |
522 | |
523 | /* Cancel pending I/O and free all TX/RX buffers */ |
524 | url_stop(ifp, 1); |
525 | |
526 | eaddr = CLLADDR(ifp->if_sadl); |
527 | for (i = 0; i < ETHER_ADDR_LEN; i++) |
528 | url_csr_write_1(sc, URL_IDR0 + i, eaddr[i]); |
529 | |
530 | /* Init transmission control register */ |
531 | URL_CLRBIT(sc, URL_TCR, |
532 | URL_TCR_TXRR1 | URL_TCR_TXRR0 | |
533 | URL_TCR_IFG1 | URL_TCR_IFG0 | |
534 | URL_TCR_NOCRC); |
535 | |
536 | /* Init receive control register */ |
537 | URL_SETBIT2(sc, URL_RCR, URL_RCR_TAIL | URL_RCR_AD); |
538 | if (ifp->if_flags & IFF_BROADCAST) |
539 | URL_SETBIT2(sc, URL_RCR, URL_RCR_AB); |
540 | else |
541 | URL_CLRBIT2(sc, URL_RCR, URL_RCR_AB); |
542 | |
543 | /* If we want promiscuous mode, accept all physical frames. */ |
544 | if (ifp->if_flags & IFF_PROMISC) |
545 | URL_SETBIT2(sc, URL_RCR, URL_RCR_AAM|URL_RCR_AAP); |
546 | else |
547 | URL_CLRBIT2(sc, URL_RCR, URL_RCR_AAM|URL_RCR_AAP); |
548 | |
549 | |
550 | /* Load the multicast filter */ |
551 | url_setmulti(sc); |
552 | |
553 | /* Enable RX and TX */ |
554 | URL_SETBIT(sc, URL_CR, URL_CR_TE | URL_CR_RE); |
555 | |
556 | if ((rc = mii_mediachg(mii)) == ENXIO) |
557 | rc = 0; |
558 | else if (rc != 0) |
559 | goto out; |
560 | |
561 | if (sc->sc_pipe_tx == NULL || sc->sc_pipe_rx == NULL) { |
562 | if (url_openpipes(sc)) { |
563 | splx(s); |
564 | return EIO; |
565 | } |
566 | } |
567 | /* Initialize transmit ring */ |
568 | if (url_tx_list_init(sc)) { |
569 | printf("%s: tx list init failed\n" , device_xname(sc->sc_dev)); |
570 | splx(s); |
571 | return EIO; |
572 | } |
573 | |
574 | /* Initialize receive ring */ |
575 | if (url_rx_list_init(sc)) { |
576 | printf("%s: rx list init failed\n" , device_xname(sc->sc_dev)); |
577 | splx(s); |
578 | return EIO; |
579 | } |
580 | /* Start up the receive pipe. */ |
581 | for (i = 0; i < URL_RX_LIST_CNT; i++) { |
582 | struct url_chain *c = &sc->sc_cdata.url_rx_chain[i]; |
583 | |
584 | usbd_setup_xfer(c->url_xfer, c, c->url_buf, URL_BUFSZ, |
585 | USBD_SHORT_XFER_OK, USBD_NO_TIMEOUT, url_rxeof); |
586 | (void)usbd_transfer(c->url_xfer); |
587 | DPRINTF(("%s: %s: start read\n" , device_xname(sc->sc_dev), |
588 | __func__)); |
589 | } |
590 | |
591 | ifp->if_flags |= IFF_RUNNING; |
592 | ifp->if_flags &= ~IFF_OACTIVE; |
593 | |
594 | callout_reset(&sc->sc_stat_ch, hz, url_tick, sc); |
595 | |
596 | out: |
597 | splx(s); |
598 | return rc; |
599 | } |
600 | |
601 | Static void |
602 | url_reset(struct url_softc *sc) |
603 | { |
604 | int i; |
605 | |
606 | DPRINTF(("%s: %s: enter\n" , device_xname(sc->sc_dev), __func__)); |
607 | |
608 | if (sc->sc_dying) |
609 | return; |
610 | |
611 | URL_SETBIT(sc, URL_CR, URL_CR_SOFT_RST); |
612 | |
613 | for (i = 0; i < URL_TX_TIMEOUT; i++) { |
614 | if (!(url_csr_read_1(sc, URL_CR) & URL_CR_SOFT_RST)) |
615 | break; |
616 | delay(10); /* XXX */ |
617 | } |
618 | |
619 | delay(10000); /* XXX */ |
620 | } |
621 | |
622 | int |
623 | url_activate(device_t self, enum devact act) |
624 | { |
625 | struct url_softc *sc = device_private(self); |
626 | |
627 | DPRINTF(("%s: %s: enter, act=%d\n" , device_xname(sc->sc_dev), |
628 | __func__, act)); |
629 | |
630 | switch (act) { |
631 | case DVACT_DEACTIVATE: |
632 | if_deactivate(&sc->sc_ec.ec_if); |
633 | sc->sc_dying = 1; |
634 | return 0; |
635 | default: |
636 | return EOPNOTSUPP; |
637 | } |
638 | } |
639 | |
640 | #define url_calchash(addr) (ether_crc32_be((addr), ETHER_ADDR_LEN) >> 26) |
641 | |
642 | |
643 | Static void |
644 | url_setmulti(struct url_softc *sc) |
645 | { |
646 | struct ifnet *ifp; |
647 | struct ether_multi *enm; |
648 | struct ether_multistep step; |
649 | uint32_t hashes[2] = { 0, 0 }; |
650 | int h = 0; |
651 | int mcnt = 0; |
652 | |
653 | DPRINTF(("%s: %s: enter\n" , device_xname(sc->sc_dev), __func__)); |
654 | |
655 | if (sc->sc_dying) |
656 | return; |
657 | |
658 | ifp = GET_IFP(sc); |
659 | |
660 | if (ifp->if_flags & IFF_PROMISC) { |
661 | URL_SETBIT2(sc, URL_RCR, URL_RCR_AAM|URL_RCR_AAP); |
662 | return; |
663 | } else if (ifp->if_flags & IFF_ALLMULTI) { |
664 | allmulti: |
665 | ifp->if_flags |= IFF_ALLMULTI; |
666 | URL_SETBIT2(sc, URL_RCR, URL_RCR_AAM); |
667 | URL_CLRBIT2(sc, URL_RCR, URL_RCR_AAP); |
668 | return; |
669 | } |
670 | |
671 | /* first, zot all the existing hash bits */ |
672 | url_csr_write_4(sc, URL_MAR0, 0); |
673 | url_csr_write_4(sc, URL_MAR4, 0); |
674 | |
675 | /* now program new ones */ |
676 | ETHER_FIRST_MULTI(step, &sc->sc_ec, enm); |
677 | while (enm != NULL) { |
678 | if (memcmp(enm->enm_addrlo, enm->enm_addrhi, |
679 | ETHER_ADDR_LEN) != 0) |
680 | goto allmulti; |
681 | |
682 | h = url_calchash(enm->enm_addrlo); |
683 | if (h < 32) |
684 | hashes[0] |= (1 << h); |
685 | else |
686 | hashes[1] |= (1 << (h -32)); |
687 | mcnt++; |
688 | ETHER_NEXT_MULTI(step, enm); |
689 | } |
690 | |
691 | ifp->if_flags &= ~IFF_ALLMULTI; |
692 | |
693 | URL_CLRBIT2(sc, URL_RCR, URL_RCR_AAM|URL_RCR_AAP); |
694 | |
695 | if (mcnt){ |
696 | URL_SETBIT2(sc, URL_RCR, URL_RCR_AM); |
697 | } else { |
698 | URL_CLRBIT2(sc, URL_RCR, URL_RCR_AM); |
699 | } |
700 | url_csr_write_4(sc, URL_MAR0, hashes[0]); |
701 | url_csr_write_4(sc, URL_MAR4, hashes[1]); |
702 | } |
703 | |
704 | Static int |
705 | url_openpipes(struct url_softc *sc) |
706 | { |
707 | usbd_status err; |
708 | int error = 0; |
709 | |
710 | if (sc->sc_dying) |
711 | return EIO; |
712 | |
713 | sc->sc_refcnt++; |
714 | |
715 | /* Open RX pipe */ |
716 | err = usbd_open_pipe(sc->sc_ctl_iface, sc->sc_bulkin_no, |
717 | USBD_EXCLUSIVE_USE, &sc->sc_pipe_rx); |
718 | if (err) { |
719 | printf("%s: open rx pipe failed: %s\n" , |
720 | device_xname(sc->sc_dev), usbd_errstr(err)); |
721 | error = EIO; |
722 | goto done; |
723 | } |
724 | |
725 | /* Open TX pipe */ |
726 | err = usbd_open_pipe(sc->sc_ctl_iface, sc->sc_bulkout_no, |
727 | USBD_EXCLUSIVE_USE, &sc->sc_pipe_tx); |
728 | if (err) { |
729 | printf("%s: open tx pipe failed: %s\n" , |
730 | device_xname(sc->sc_dev), usbd_errstr(err)); |
731 | error = EIO; |
732 | goto done; |
733 | } |
734 | |
735 | #if 0 |
736 | /* XXX: interrupt endpoint is not yet supported */ |
737 | /* Open Interrupt pipe */ |
738 | err = usbd_open_pipe_intr(sc->sc_ctl_iface, sc->sc_intrin_no, |
739 | USBD_EXCLUSIVE_USE, &sc->sc_pipe_intr, sc, |
740 | &sc->sc_cdata.url_ibuf, URL_INTR_PKGLEN, |
741 | url_intr, USBD_DEFAULT_INTERVAL); |
742 | if (err) { |
743 | printf("%s: open intr pipe failed: %s\n" , |
744 | device_xname(sc->sc_dev), usbd_errstr(err)); |
745 | error = EIO; |
746 | goto done; |
747 | } |
748 | #endif |
749 | |
750 | done: |
751 | if (--sc->sc_refcnt < 0) |
752 | usb_detach_wakeupold(sc->sc_dev); |
753 | |
754 | return error; |
755 | } |
756 | |
757 | Static int |
758 | url_newbuf(struct url_softc *sc, struct url_chain *c, struct mbuf *m) |
759 | { |
760 | struct mbuf *m_new = NULL; |
761 | |
762 | DPRINTF(("%s: %s: enter\n" , device_xname(sc->sc_dev), __func__)); |
763 | |
764 | if (m == NULL) { |
765 | MGETHDR(m_new, M_DONTWAIT, MT_DATA); |
766 | if (m_new == NULL) { |
767 | printf("%s: no memory for rx list " |
768 | "-- packet dropped!\n" , device_xname(sc->sc_dev)); |
769 | return ENOBUFS; |
770 | } |
771 | MCLGET(m_new, M_DONTWAIT); |
772 | if (!(m_new->m_flags & M_EXT)) { |
773 | printf("%s: no memory for rx list " |
774 | "-- packet dropped!\n" , device_xname(sc->sc_dev)); |
775 | m_freem(m_new); |
776 | return ENOBUFS; |
777 | } |
778 | m_new->m_len = m_new->m_pkthdr.len = MCLBYTES; |
779 | } else { |
780 | m_new = m; |
781 | m_new->m_len = m_new->m_pkthdr.len = MCLBYTES; |
782 | m_new->m_data = m_new->m_ext.ext_buf; |
783 | } |
784 | |
785 | m_adj(m_new, ETHER_ALIGN); |
786 | c->url_mbuf = m_new; |
787 | |
788 | return 0; |
789 | } |
790 | |
791 | |
792 | Static int |
793 | url_rx_list_init(struct url_softc *sc) |
794 | { |
795 | struct url_cdata *cd; |
796 | struct url_chain *c; |
797 | int i; |
798 | |
799 | DPRINTF(("%s: %s: enter\n" , device_xname(sc->sc_dev), __func__)); |
800 | |
801 | cd = &sc->sc_cdata; |
802 | for (i = 0; i < URL_RX_LIST_CNT; i++) { |
803 | c = &cd->url_rx_chain[i]; |
804 | c->url_sc = sc; |
805 | c->url_idx = i; |
806 | if (url_newbuf(sc, c, NULL) == ENOBUFS) |
807 | return ENOBUFS; |
808 | if (c->url_xfer == NULL) { |
809 | int error = usbd_create_xfer(sc->sc_pipe_rx, URL_BUFSZ, |
810 | USBD_SHORT_XFER_OK, 0, &c->url_xfer); |
811 | if (error) |
812 | return error; |
813 | c->url_buf = usbd_get_buffer(c->url_xfer); |
814 | } |
815 | } |
816 | |
817 | return 0; |
818 | } |
819 | |
820 | Static int |
821 | url_tx_list_init(struct url_softc *sc) |
822 | { |
823 | struct url_cdata *cd; |
824 | struct url_chain *c; |
825 | int i; |
826 | |
827 | DPRINTF(("%s: %s: enter\n" , device_xname(sc->sc_dev), __func__)); |
828 | |
829 | cd = &sc->sc_cdata; |
830 | for (i = 0; i < URL_TX_LIST_CNT; i++) { |
831 | c = &cd->url_tx_chain[i]; |
832 | c->url_sc = sc; |
833 | c->url_idx = i; |
834 | c->url_mbuf = NULL; |
835 | if (c->url_xfer == NULL) { |
836 | int error = usbd_create_xfer(sc->sc_pipe_tx, URL_BUFSZ, |
837 | USBD_FORCE_SHORT_XFER, 0, &c->url_xfer); |
838 | if (error) |
839 | return error; |
840 | c->url_buf = usbd_get_buffer(c->url_xfer); |
841 | } |
842 | } |
843 | |
844 | return 0; |
845 | } |
846 | |
847 | Static void |
848 | url_start(struct ifnet *ifp) |
849 | { |
850 | struct url_softc *sc = ifp->if_softc; |
851 | struct mbuf *m_head = NULL; |
852 | |
853 | DPRINTF(("%s: %s: enter, link=%d\n" , device_xname(sc->sc_dev), |
854 | __func__, sc->sc_link)); |
855 | |
856 | if (sc->sc_dying) |
857 | return; |
858 | |
859 | if (!sc->sc_link) |
860 | return; |
861 | |
862 | if (ifp->if_flags & IFF_OACTIVE) |
863 | return; |
864 | |
865 | IFQ_POLL(&ifp->if_snd, m_head); |
866 | if (m_head == NULL) |
867 | return; |
868 | |
869 | if (url_send(sc, m_head, 0)) { |
870 | ifp->if_flags |= IFF_OACTIVE; |
871 | return; |
872 | } |
873 | |
874 | IFQ_DEQUEUE(&ifp->if_snd, m_head); |
875 | |
876 | bpf_mtap(ifp, m_head); |
877 | |
878 | ifp->if_flags |= IFF_OACTIVE; |
879 | |
880 | /* Set a timeout in case the chip goes out to lunch. */ |
881 | ifp->if_timer = 5; |
882 | } |
883 | |
884 | Static int |
885 | url_send(struct url_softc *sc, struct mbuf *m, int idx) |
886 | { |
887 | int total_len; |
888 | struct url_chain *c; |
889 | usbd_status err; |
890 | |
891 | DPRINTF(("%s: %s: enter\n" , device_xname(sc->sc_dev),__func__)); |
892 | |
893 | c = &sc->sc_cdata.url_tx_chain[idx]; |
894 | |
895 | /* Copy the mbuf data into a contiguous buffer */ |
896 | m_copydata(m, 0, m->m_pkthdr.len, c->url_buf); |
897 | c->url_mbuf = m; |
898 | total_len = m->m_pkthdr.len; |
899 | |
900 | if (total_len < URL_MIN_FRAME_LEN) { |
901 | memset(c->url_buf + total_len, 0, |
902 | URL_MIN_FRAME_LEN - total_len); |
903 | total_len = URL_MIN_FRAME_LEN; |
904 | } |
905 | usbd_setup_xfer(c->url_xfer,c, c->url_buf, total_len, |
906 | USBD_FORCE_SHORT_XFER, URL_TX_TIMEOUT, url_txeof); |
907 | |
908 | /* Transmit */ |
909 | sc->sc_refcnt++; |
910 | err = usbd_transfer(c->url_xfer); |
911 | if (--sc->sc_refcnt < 0) |
912 | usb_detach_wakeupold(sc->sc_dev); |
913 | if (err != USBD_IN_PROGRESS) { |
914 | printf("%s: url_send error=%s\n" , device_xname(sc->sc_dev), |
915 | usbd_errstr(err)); |
916 | /* Stop the interface */ |
917 | usb_add_task(sc->sc_udev, &sc->sc_stop_task, |
918 | USB_TASKQ_DRIVER); |
919 | return EIO; |
920 | } |
921 | |
922 | DPRINTF(("%s: %s: send %d bytes\n" , device_xname(sc->sc_dev), |
923 | __func__, total_len)); |
924 | |
925 | sc->sc_cdata.url_tx_cnt++; |
926 | |
927 | return 0; |
928 | } |
929 | |
930 | Static void |
931 | url_txeof(struct usbd_xfer *xfer, void *priv, |
932 | usbd_status status) |
933 | { |
934 | struct url_chain *c = priv; |
935 | struct url_softc *sc = c->url_sc; |
936 | struct ifnet *ifp = GET_IFP(sc); |
937 | int s; |
938 | |
939 | if (sc->sc_dying) |
940 | return; |
941 | |
942 | s = splnet(); |
943 | |
944 | DPRINTF(("%s: %s: enter\n" , device_xname(sc->sc_dev), __func__)); |
945 | |
946 | ifp->if_timer = 0; |
947 | ifp->if_flags &= ~IFF_OACTIVE; |
948 | |
949 | if (status != USBD_NORMAL_COMPLETION) { |
950 | if (status == USBD_NOT_STARTED || status == USBD_CANCELLED) { |
951 | splx(s); |
952 | return; |
953 | } |
954 | ifp->if_oerrors++; |
955 | printf("%s: usb error on tx: %s\n" , device_xname(sc->sc_dev), |
956 | usbd_errstr(status)); |
957 | if (status == USBD_STALLED) { |
958 | sc->sc_refcnt++; |
959 | usbd_clear_endpoint_stall_async(sc->sc_pipe_tx); |
960 | if (--sc->sc_refcnt < 0) |
961 | usb_detach_wakeupold(sc->sc_dev); |
962 | } |
963 | splx(s); |
964 | return; |
965 | } |
966 | |
967 | ifp->if_opackets++; |
968 | |
969 | m_freem(c->url_mbuf); |
970 | c->url_mbuf = NULL; |
971 | |
972 | if (IFQ_IS_EMPTY(&ifp->if_snd) == 0) |
973 | url_start(ifp); |
974 | |
975 | splx(s); |
976 | } |
977 | |
978 | Static void |
979 | url_rxeof(struct usbd_xfer *xfer, void *priv, usbd_status status) |
980 | { |
981 | struct url_chain *c = priv; |
982 | struct url_softc *sc = c->url_sc; |
983 | struct ifnet *ifp = GET_IFP(sc); |
984 | struct mbuf *m; |
985 | uint32_t total_len; |
986 | url_rxhdr_t rxhdr; |
987 | int s; |
988 | |
989 | DPRINTF(("%s: %s: enter\n" , device_xname(sc->sc_dev),__func__)); |
990 | |
991 | if (sc->sc_dying) |
992 | return; |
993 | |
994 | if (status != USBD_NORMAL_COMPLETION) { |
995 | if (status == USBD_NOT_STARTED || status == USBD_CANCELLED) |
996 | return; |
997 | sc->sc_rx_errs++; |
998 | if (usbd_ratecheck(&sc->sc_rx_notice)) { |
999 | printf("%s: %u usb errors on rx: %s\n" , |
1000 | device_xname(sc->sc_dev), sc->sc_rx_errs, |
1001 | usbd_errstr(status)); |
1002 | sc->sc_rx_errs = 0; |
1003 | } |
1004 | if (status == USBD_STALLED) { |
1005 | sc->sc_refcnt++; |
1006 | usbd_clear_endpoint_stall_async(sc->sc_pipe_rx); |
1007 | if (--sc->sc_refcnt < 0) |
1008 | usb_detach_wakeupold(sc->sc_dev); |
1009 | } |
1010 | goto done; |
1011 | } |
1012 | |
1013 | usbd_get_xfer_status(xfer, NULL, NULL, &total_len, NULL); |
1014 | |
1015 | memcpy(mtod(c->url_mbuf, char *), c->url_buf, total_len); |
1016 | |
1017 | if (total_len <= ETHER_CRC_LEN) { |
1018 | ifp->if_ierrors++; |
1019 | goto done; |
1020 | } |
1021 | |
1022 | memcpy(&rxhdr, c->url_buf + total_len - ETHER_CRC_LEN, sizeof(rxhdr)); |
1023 | |
1024 | DPRINTF(("%s: RX Status: %dbytes%s%s%s%s packets\n" , |
1025 | device_xname(sc->sc_dev), |
1026 | UGETW(rxhdr) & URL_RXHDR_BYTEC_MASK, |
1027 | UGETW(rxhdr) & URL_RXHDR_VALID_MASK ? ", Valid" : "" , |
1028 | UGETW(rxhdr) & URL_RXHDR_RUNTPKT_MASK ? ", Runt" : "" , |
1029 | UGETW(rxhdr) & URL_RXHDR_PHYPKT_MASK ? ", Physical match" : "" , |
1030 | UGETW(rxhdr) & URL_RXHDR_MCASTPKT_MASK ? ", Multicast" : "" )); |
1031 | |
1032 | if ((UGETW(rxhdr) & URL_RXHDR_VALID_MASK) == 0) { |
1033 | ifp->if_ierrors++; |
1034 | goto done; |
1035 | } |
1036 | |
1037 | ifp->if_ipackets++; |
1038 | total_len -= ETHER_CRC_LEN; |
1039 | |
1040 | m = c->url_mbuf; |
1041 | m->m_pkthdr.len = m->m_len = total_len; |
1042 | m_set_rcvif(m, ifp); |
1043 | |
1044 | s = splnet(); |
1045 | |
1046 | if (url_newbuf(sc, c, NULL) == ENOBUFS) { |
1047 | ifp->if_ierrors++; |
1048 | goto done1; |
1049 | } |
1050 | |
1051 | bpf_mtap(ifp, m); |
1052 | |
1053 | DPRINTF(("%s: %s: deliver %d\n" , device_xname(sc->sc_dev), |
1054 | __func__, m->m_len)); |
1055 | if_percpuq_enqueue((ifp)->if_percpuq, (m)); |
1056 | |
1057 | done1: |
1058 | splx(s); |
1059 | |
1060 | done: |
1061 | /* Setup new transfer */ |
1062 | usbd_setup_xfer(xfer, c, c->url_buf, URL_BUFSZ, USBD_SHORT_XFER_OK, |
1063 | USBD_NO_TIMEOUT, url_rxeof); |
1064 | sc->sc_refcnt++; |
1065 | usbd_transfer(xfer); |
1066 | if (--sc->sc_refcnt < 0) |
1067 | usb_detach_wakeupold(sc->sc_dev); |
1068 | |
1069 | DPRINTF(("%s: %s: start rx\n" , device_xname(sc->sc_dev), __func__)); |
1070 | } |
1071 | |
1072 | #if 0 |
1073 | Static void url_intr(void) |
1074 | { |
1075 | } |
1076 | #endif |
1077 | |
1078 | Static int |
1079 | url_ioctl(struct ifnet *ifp, u_long cmd, void *data) |
1080 | { |
1081 | struct url_softc *sc = ifp->if_softc; |
1082 | int s, error = 0; |
1083 | |
1084 | DPRINTF(("%s: %s: enter\n" , device_xname(sc->sc_dev), __func__)); |
1085 | |
1086 | if (sc->sc_dying) |
1087 | return EIO; |
1088 | |
1089 | s = splnet(); |
1090 | |
1091 | error = ether_ioctl(ifp, cmd, data); |
1092 | if (error == ENETRESET) { |
1093 | if (ifp->if_flags & IFF_RUNNING) |
1094 | url_setmulti(sc); |
1095 | error = 0; |
1096 | } |
1097 | |
1098 | splx(s); |
1099 | |
1100 | return error; |
1101 | } |
1102 | |
1103 | Static void |
1104 | url_watchdog(struct ifnet *ifp) |
1105 | { |
1106 | struct url_softc *sc = ifp->if_softc; |
1107 | struct url_chain *c; |
1108 | usbd_status stat; |
1109 | int s; |
1110 | |
1111 | DPRINTF(("%s: %s: enter\n" , device_xname(sc->sc_dev), __func__)); |
1112 | |
1113 | ifp->if_oerrors++; |
1114 | printf("%s: watchdog timeout\n" , device_xname(sc->sc_dev)); |
1115 | |
1116 | s = splusb(); |
1117 | c = &sc->sc_cdata.url_tx_chain[0]; |
1118 | usbd_get_xfer_status(c->url_xfer, NULL, NULL, NULL, &stat); |
1119 | url_txeof(c->url_xfer, c, stat); |
1120 | |
1121 | if (IFQ_IS_EMPTY(&ifp->if_snd) == 0) |
1122 | url_start(ifp); |
1123 | splx(s); |
1124 | } |
1125 | |
1126 | Static void |
1127 | url_stop_task(struct url_softc *sc) |
1128 | { |
1129 | url_stop(GET_IFP(sc), 1); |
1130 | } |
1131 | |
1132 | /* Stop the adapter and free any mbufs allocated to the RX and TX lists. */ |
1133 | Static void |
1134 | url_stop(struct ifnet *ifp, int disable) |
1135 | { |
1136 | struct url_softc *sc = ifp->if_softc; |
1137 | usbd_status err; |
1138 | int i; |
1139 | |
1140 | DPRINTF(("%s: %s: enter\n" , device_xname(sc->sc_dev), __func__)); |
1141 | |
1142 | ifp->if_timer = 0; |
1143 | |
1144 | url_reset(sc); |
1145 | |
1146 | callout_stop(&sc->sc_stat_ch); |
1147 | |
1148 | /* Stop transfers */ |
1149 | /* RX endpoint */ |
1150 | if (sc->sc_pipe_rx != NULL) { |
1151 | err = usbd_abort_pipe(sc->sc_pipe_rx); |
1152 | if (err) |
1153 | printf("%s: abort rx pipe failed: %s\n" , |
1154 | device_xname(sc->sc_dev), usbd_errstr(err)); |
1155 | } |
1156 | |
1157 | /* TX endpoint */ |
1158 | if (sc->sc_pipe_tx != NULL) { |
1159 | err = usbd_abort_pipe(sc->sc_pipe_tx); |
1160 | if (err) |
1161 | printf("%s: abort tx pipe failed: %s\n" , |
1162 | device_xname(sc->sc_dev), usbd_errstr(err)); |
1163 | } |
1164 | |
1165 | #if 0 |
1166 | /* XXX: Interrupt endpoint is not yet supported!! */ |
1167 | /* Interrupt endpoint */ |
1168 | if (sc->sc_pipe_intr != NULL) { |
1169 | err = usbd_abort_pipe(sc->sc_pipe_intr); |
1170 | if (err) |
1171 | printf("%s: abort intr pipe failed: %s\n" , |
1172 | device_xname(sc->sc_dev), usbd_errstr(err)); |
1173 | err = usbd_close_pipe(sc->sc_pipe_intr); |
1174 | if (err) |
1175 | printf("%s: close intr pipe failed: %s\n" , |
1176 | device_xname(sc->sc_dev), usbd_errstr(err)); |
1177 | sc->sc_pipe_intr = NULL; |
1178 | } |
1179 | #endif |
1180 | |
1181 | /* Free RX resources. */ |
1182 | for (i = 0; i < URL_RX_LIST_CNT; i++) { |
1183 | if (sc->sc_cdata.url_rx_chain[i].url_mbuf != NULL) { |
1184 | m_freem(sc->sc_cdata.url_rx_chain[i].url_mbuf); |
1185 | sc->sc_cdata.url_rx_chain[i].url_mbuf = NULL; |
1186 | } |
1187 | if (sc->sc_cdata.url_rx_chain[i].url_xfer != NULL) { |
1188 | usbd_destroy_xfer(sc->sc_cdata.url_rx_chain[i].url_xfer); |
1189 | sc->sc_cdata.url_rx_chain[i].url_xfer = NULL; |
1190 | } |
1191 | } |
1192 | |
1193 | /* Free TX resources. */ |
1194 | for (i = 0; i < URL_TX_LIST_CNT; i++) { |
1195 | if (sc->sc_cdata.url_tx_chain[i].url_mbuf != NULL) { |
1196 | m_freem(sc->sc_cdata.url_tx_chain[i].url_mbuf); |
1197 | sc->sc_cdata.url_tx_chain[i].url_mbuf = NULL; |
1198 | } |
1199 | if (sc->sc_cdata.url_tx_chain[i].url_xfer != NULL) { |
1200 | usbd_destroy_xfer(sc->sc_cdata.url_tx_chain[i].url_xfer); |
1201 | sc->sc_cdata.url_tx_chain[i].url_xfer = NULL; |
1202 | } |
1203 | } |
1204 | |
1205 | /* Close pipes */ |
1206 | /* RX endpoint */ |
1207 | if (sc->sc_pipe_rx != NULL) { |
1208 | err = usbd_close_pipe(sc->sc_pipe_rx); |
1209 | if (err) |
1210 | printf("%s: close rx pipe failed: %s\n" , |
1211 | device_xname(sc->sc_dev), usbd_errstr(err)); |
1212 | sc->sc_pipe_rx = NULL; |
1213 | } |
1214 | |
1215 | /* TX endpoint */ |
1216 | if (sc->sc_pipe_tx != NULL) { |
1217 | err = usbd_close_pipe(sc->sc_pipe_tx); |
1218 | if (err) |
1219 | printf("%s: close tx pipe failed: %s\n" , |
1220 | device_xname(sc->sc_dev), usbd_errstr(err)); |
1221 | sc->sc_pipe_tx = NULL; |
1222 | } |
1223 | |
1224 | sc->sc_link = 0; |
1225 | ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE); |
1226 | } |
1227 | |
1228 | /* Set media options */ |
1229 | Static int |
1230 | url_ifmedia_change(struct ifnet *ifp) |
1231 | { |
1232 | struct url_softc *sc = ifp->if_softc; |
1233 | struct mii_data *mii = GET_MII(sc); |
1234 | int rc; |
1235 | |
1236 | DPRINTF(("%s: %s: enter\n" , device_xname(sc->sc_dev), __func__)); |
1237 | |
1238 | if (sc->sc_dying) |
1239 | return 0; |
1240 | |
1241 | sc->sc_link = 0; |
1242 | if ((rc = mii_mediachg(mii)) == ENXIO) |
1243 | return 0; |
1244 | return rc; |
1245 | } |
1246 | |
1247 | /* Report current media status. */ |
1248 | Static void |
1249 | url_ifmedia_status(struct ifnet *ifp, struct ifmediareq *ifmr) |
1250 | { |
1251 | struct url_softc *sc = ifp->if_softc; |
1252 | |
1253 | DPRINTF(("%s: %s: enter\n" , device_xname(sc->sc_dev), __func__)); |
1254 | |
1255 | if (sc->sc_dying) |
1256 | return; |
1257 | |
1258 | ether_mediastatus(ifp, ifmr); |
1259 | } |
1260 | |
1261 | Static void |
1262 | url_tick(void *xsc) |
1263 | { |
1264 | struct url_softc *sc = xsc; |
1265 | |
1266 | if (sc == NULL) |
1267 | return; |
1268 | |
1269 | DPRINTFN(0xff, ("%s: %s: enter\n" , device_xname(sc->sc_dev), |
1270 | __func__)); |
1271 | |
1272 | if (sc->sc_dying) |
1273 | return; |
1274 | |
1275 | /* Perform periodic stuff in process context */ |
1276 | usb_add_task(sc->sc_udev, &sc->sc_tick_task, USB_TASKQ_DRIVER); |
1277 | } |
1278 | |
1279 | Static void |
1280 | url_tick_task(void *xsc) |
1281 | { |
1282 | struct url_softc *sc = xsc; |
1283 | struct ifnet *ifp; |
1284 | struct mii_data *mii; |
1285 | int s; |
1286 | |
1287 | if (sc == NULL) |
1288 | return; |
1289 | |
1290 | DPRINTFN(0xff, ("%s: %s: enter\n" , device_xname(sc->sc_dev), |
1291 | __func__)); |
1292 | |
1293 | if (sc->sc_dying) |
1294 | return; |
1295 | |
1296 | ifp = GET_IFP(sc); |
1297 | mii = GET_MII(sc); |
1298 | |
1299 | if (mii == NULL) |
1300 | return; |
1301 | |
1302 | s = splnet(); |
1303 | |
1304 | mii_tick(mii); |
1305 | if (!sc->sc_link) { |
1306 | mii_pollstat(mii); |
1307 | if (mii->mii_media_status & IFM_ACTIVE && |
1308 | IFM_SUBTYPE(mii->mii_media_active) != IFM_NONE) { |
1309 | DPRINTF(("%s: %s: got link\n" , |
1310 | device_xname(sc->sc_dev), __func__)); |
1311 | sc->sc_link++; |
1312 | if (IFQ_IS_EMPTY(&ifp->if_snd) == 0) |
1313 | url_start(ifp); |
1314 | } |
1315 | } |
1316 | |
1317 | callout_reset(&sc->sc_stat_ch, hz, url_tick, sc); |
1318 | |
1319 | splx(s); |
1320 | } |
1321 | |
1322 | /* Get exclusive access to the MII registers */ |
1323 | Static void |
1324 | url_lock_mii(struct url_softc *sc) |
1325 | { |
1326 | DPRINTFN(0xff, ("%s: %s: enter\n" , device_xname(sc->sc_dev), |
1327 | __func__)); |
1328 | |
1329 | sc->sc_refcnt++; |
1330 | rw_enter(&sc->sc_mii_rwlock, RW_WRITER); |
1331 | } |
1332 | |
1333 | Static void |
1334 | url_unlock_mii(struct url_softc *sc) |
1335 | { |
1336 | DPRINTFN(0xff, ("%s: %s: enter\n" , device_xname(sc->sc_dev), |
1337 | __func__)); |
1338 | |
1339 | rw_exit(&sc->sc_mii_rwlock); |
1340 | if (--sc->sc_refcnt < 0) |
1341 | usb_detach_wakeupold(sc->sc_dev); |
1342 | } |
1343 | |
1344 | Static int |
1345 | url_int_miibus_readreg(device_t dev, int phy, int reg) |
1346 | { |
1347 | struct url_softc *sc; |
1348 | uint16_t val; |
1349 | |
1350 | if (dev == NULL) |
1351 | return 0; |
1352 | |
1353 | sc = device_private(dev); |
1354 | |
1355 | DPRINTFN(0xff, ("%s: %s: enter, phy=%d reg=0x%04x\n" , |
1356 | device_xname(sc->sc_dev), __func__, phy, reg)); |
1357 | |
1358 | if (sc->sc_dying) { |
1359 | #ifdef DIAGNOSTIC |
1360 | printf("%s: %s: dying\n" , device_xname(sc->sc_dev), |
1361 | __func__); |
1362 | #endif |
1363 | return 0; |
1364 | } |
1365 | |
1366 | /* XXX: one PHY only for the RTL8150 internal PHY */ |
1367 | if (phy != 0) { |
1368 | DPRINTFN(0xff, ("%s: %s: phy=%d is not supported\n" , |
1369 | device_xname(sc->sc_dev), __func__, phy)); |
1370 | return 0; |
1371 | } |
1372 | |
1373 | url_lock_mii(sc); |
1374 | |
1375 | switch (reg) { |
1376 | case MII_BMCR: /* Control Register */ |
1377 | reg = URL_BMCR; |
1378 | break; |
1379 | case MII_BMSR: /* Status Register */ |
1380 | reg = URL_BMSR; |
1381 | break; |
1382 | case MII_PHYIDR1: |
1383 | case MII_PHYIDR2: |
1384 | val = 0; |
1385 | goto R_DONE; |
1386 | break; |
1387 | case MII_ANAR: /* Autonegotiation advertisement */ |
1388 | reg = URL_ANAR; |
1389 | break; |
1390 | case MII_ANLPAR: /* Autonegotiation link partner abilities */ |
1391 | reg = URL_ANLP; |
1392 | break; |
1393 | case URLPHY_MSR: /* Media Status Register */ |
1394 | reg = URL_MSR; |
1395 | break; |
1396 | default: |
1397 | printf("%s: %s: bad register %04x\n" , |
1398 | device_xname(sc->sc_dev), __func__, reg); |
1399 | val = 0; |
1400 | goto R_DONE; |
1401 | break; |
1402 | } |
1403 | |
1404 | if (reg == URL_MSR) |
1405 | val = url_csr_read_1(sc, reg); |
1406 | else |
1407 | val = url_csr_read_2(sc, reg); |
1408 | |
1409 | R_DONE: |
1410 | DPRINTFN(0xff, ("%s: %s: phy=%d reg=0x%04x => 0x%04x\n" , |
1411 | device_xname(sc->sc_dev), __func__, phy, reg, val)); |
1412 | |
1413 | url_unlock_mii(sc); |
1414 | return val; |
1415 | } |
1416 | |
1417 | Static void |
1418 | url_int_miibus_writereg(device_t dev, int phy, int reg, int data) |
1419 | { |
1420 | struct url_softc *sc; |
1421 | |
1422 | if (dev == NULL) |
1423 | return; |
1424 | |
1425 | sc = device_private(dev); |
1426 | |
1427 | DPRINTFN(0xff, ("%s: %s: enter, phy=%d reg=0x%04x data=0x%04x\n" , |
1428 | device_xname(sc->sc_dev), __func__, phy, reg, data)); |
1429 | |
1430 | if (sc->sc_dying) { |
1431 | #ifdef DIAGNOSTIC |
1432 | printf("%s: %s: dying\n" , device_xname(sc->sc_dev), |
1433 | __func__); |
1434 | #endif |
1435 | return; |
1436 | } |
1437 | |
1438 | /* XXX: one PHY only for the RTL8150 internal PHY */ |
1439 | if (phy != 0) { |
1440 | DPRINTFN(0xff, ("%s: %s: phy=%d is not supported\n" , |
1441 | device_xname(sc->sc_dev), __func__, phy)); |
1442 | return; |
1443 | } |
1444 | |
1445 | url_lock_mii(sc); |
1446 | |
1447 | switch (reg) { |
1448 | case MII_BMCR: /* Control Register */ |
1449 | reg = URL_BMCR; |
1450 | break; |
1451 | case MII_BMSR: /* Status Register */ |
1452 | reg = URL_BMSR; |
1453 | break; |
1454 | case MII_PHYIDR1: |
1455 | case MII_PHYIDR2: |
1456 | goto W_DONE; |
1457 | break; |
1458 | case MII_ANAR: /* Autonegotiation advertisement */ |
1459 | reg = URL_ANAR; |
1460 | break; |
1461 | case MII_ANLPAR: /* Autonegotiation link partner abilities */ |
1462 | reg = URL_ANLP; |
1463 | break; |
1464 | case URLPHY_MSR: /* Media Status Register */ |
1465 | reg = URL_MSR; |
1466 | break; |
1467 | default: |
1468 | printf("%s: %s: bad register %04x\n" , |
1469 | device_xname(sc->sc_dev), __func__, reg); |
1470 | goto W_DONE; |
1471 | break; |
1472 | } |
1473 | |
1474 | if (reg == URL_MSR) |
1475 | url_csr_write_1(sc, reg, data); |
1476 | else |
1477 | url_csr_write_2(sc, reg, data); |
1478 | W_DONE: |
1479 | |
1480 | url_unlock_mii(sc); |
1481 | return; |
1482 | } |
1483 | |
1484 | Static void |
1485 | url_miibus_statchg(struct ifnet *ifp) |
1486 | { |
1487 | #ifdef URL_DEBUG |
1488 | if (ifp == NULL) |
1489 | return; |
1490 | |
1491 | DPRINTF(("%s: %s: enter\n" , ifp->if_xname, __func__)); |
1492 | #endif |
1493 | /* Nothing to do */ |
1494 | } |
1495 | |
1496 | #if 0 |
1497 | /* |
1498 | * external PHYs support, but not test. |
1499 | */ |
1500 | Static int |
1501 | url_ext_miibus_redreg(device_t dev, int phy, int reg) |
1502 | { |
1503 | struct url_softc *sc = device_private(dev); |
1504 | uint16_t val; |
1505 | |
1506 | DPRINTF(("%s: %s: enter, phy=%d reg=0x%04x\n" , |
1507 | device_xname(sc->sc_dev), __func__, phy, reg)); |
1508 | |
1509 | if (sc->sc_dying) { |
1510 | #ifdef DIAGNOSTIC |
1511 | printf("%s: %s: dying\n" , device_xname(sc->sc_dev), |
1512 | __func__); |
1513 | #endif |
1514 | return 0; |
1515 | } |
1516 | |
1517 | url_lock_mii(sc); |
1518 | |
1519 | url_csr_write_1(sc, URL_PHYADD, phy & URL_PHYADD_MASK); |
1520 | /* |
1521 | * RTL8150L will initiate a MII management data transaction |
1522 | * if PHYCNT_OWN bit is set 1 by software. After transaction, |
1523 | * this bit is auto cleared by TRL8150L. |
1524 | */ |
1525 | url_csr_write_1(sc, URL_PHYCNT, |
1526 | (reg | URL_PHYCNT_PHYOWN) & ~URL_PHYCNT_RWCR); |
1527 | for (i = 0; i < URL_TIMEOUT; i++) { |
1528 | if ((url_csr_read_1(sc, URL_PHYCNT) & URL_PHYCNT_PHYOWN) == 0) |
1529 | break; |
1530 | } |
1531 | if (i == URL_TIMEOUT) { |
1532 | printf("%s: MII read timed out\n" , device_xname(sc->sc_dev)); |
1533 | } |
1534 | |
1535 | val = url_csr_read_2(sc, URL_PHYDAT); |
1536 | |
1537 | DPRINTF(("%s: %s: phy=%d reg=0x%04x => 0x%04x\n" , |
1538 | device_xname(sc->sc_dev), __func__, phy, reg, val)); |
1539 | |
1540 | url_unlock_mii(sc); |
1541 | return val; |
1542 | } |
1543 | |
1544 | Static void |
1545 | url_ext_miibus_writereg(device_t dev, int phy, int reg, int data) |
1546 | { |
1547 | struct url_softc *sc = device_private(dev); |
1548 | |
1549 | DPRINTF(("%s: %s: enter, phy=%d reg=0x%04x data=0x%04x\n" , |
1550 | device_xname(sc->sc_dev), __func__, phy, reg, data)); |
1551 | |
1552 | if (sc->sc_dying) { |
1553 | #ifdef DIAGNOSTIC |
1554 | printf("%s: %s: dying\n" , device_xname(sc->sc_dev), |
1555 | __func__); |
1556 | #endif |
1557 | return; |
1558 | } |
1559 | |
1560 | url_lock_mii(sc); |
1561 | |
1562 | url_csr_write_2(sc, URL_PHYDAT, data); |
1563 | url_csr_write_1(sc, URL_PHYADD, phy); |
1564 | url_csr_write_1(sc, URL_PHYCNT, reg | URL_PHYCNT_RWCR); /* Write */ |
1565 | |
1566 | for (i=0; i < URL_TIMEOUT; i++) { |
1567 | if (url_csr_read_1(sc, URL_PHYCNT) & URL_PHYCNT_PHYOWN) |
1568 | break; |
1569 | } |
1570 | |
1571 | if (i == URL_TIMEOUT) { |
1572 | printf("%s: MII write timed out\n" , |
1573 | device_xname(sc->sc_dev)); |
1574 | } |
1575 | |
1576 | url_unlock_mii(sc); |
1577 | return; |
1578 | } |
1579 | #endif |
1580 | |