1*81508fe3Sjsg /* $OpenBSD: if_ugl.c,v 1.28 2024/05/23 03:21:09 jsg Exp $ */
2e737caceSsasano /* $NetBSD: if_upl.c,v 1.19 2002/07/11 21:14:26 augustss Exp $ */
3e737caceSsasano /*
4da28f5dbSsasano * Copyright (c) 2013 SASANO Takayoshi <uaa@uaa.org.uk>
5da28f5dbSsasano *
6da28f5dbSsasano * Permission to use, copy, modify, and distribute this software for any
7da28f5dbSsasano * purpose with or without fee is hereby granted, provided that the above
8da28f5dbSsasano * copyright notice and this permission notice appear in all copies.
9da28f5dbSsasano *
10da28f5dbSsasano * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11da28f5dbSsasano * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12da28f5dbSsasano * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13da28f5dbSsasano * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14da28f5dbSsasano * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15da28f5dbSsasano * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16da28f5dbSsasano * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17da28f5dbSsasano */
18da28f5dbSsasano
19da28f5dbSsasano /*
20e737caceSsasano * Copyright (c) 2000 The NetBSD Foundation, Inc.
21e737caceSsasano * All rights reserved.
22e737caceSsasano *
23e737caceSsasano * This code is derived from software contributed to The NetBSD Foundation
24e737caceSsasano * by Lennart Augustsson (lennart@augustsson.net) at
25e737caceSsasano * Carlstedt Research & Technology.
26e737caceSsasano *
27e737caceSsasano * Redistribution and use in source and binary forms, with or without
28e737caceSsasano * modification, are permitted provided that the following conditions
29e737caceSsasano * are met:
30e737caceSsasano * 1. Redistributions of source code must retain the above copyright
31e737caceSsasano * notice, this list of conditions and the following disclaimer.
32e737caceSsasano * 2. Redistributions in binary form must reproduce the above copyright
33e737caceSsasano * notice, this list of conditions and the following disclaimer in the
34e737caceSsasano * documentation and/or other materials provided with the distribution.
35e737caceSsasano *
36e737caceSsasano * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
37e737caceSsasano * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
38e737caceSsasano * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
39e737caceSsasano * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
40e737caceSsasano * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
41e737caceSsasano * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
42e737caceSsasano * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
43e737caceSsasano * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
44e737caceSsasano * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
45e737caceSsasano * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
46e737caceSsasano * POSSIBILITY OF SUCH DAMAGE.
47e737caceSsasano */
48e737caceSsasano
49e737caceSsasano /*
50e737caceSsasano * Genesys Logic GL620USB-A driver
51e737caceSsasano * This driver is based on Prolific PL2301/PL2302 driver (if_upl.c).
52e737caceSsasano */
53e737caceSsasano
54e737caceSsasano #include <bpfilter.h>
55e737caceSsasano
56e737caceSsasano #include <sys/param.h>
57e737caceSsasano #include <sys/systm.h>
58e737caceSsasano #include <sys/timeout.h>
59e737caceSsasano #include <sys/sockio.h>
60e737caceSsasano #include <sys/mbuf.h>
61e737caceSsasano
62e737caceSsasano #include <sys/device.h>
63e737caceSsasano
64e737caceSsasano #include <net/if.h>
65e737caceSsasano
66e737caceSsasano #if NBPFILTER > 0
67e737caceSsasano #include <net/bpf.h>
68e737caceSsasano #endif
69e737caceSsasano
70e737caceSsasano #include <netinet/in.h>
71e737caceSsasano #include <netinet/if_ether.h>
72e737caceSsasano
73e737caceSsasano #include <dev/usb/usb.h>
74e737caceSsasano #include <dev/usb/usbdi.h>
75e737caceSsasano #include <dev/usb/usbdevs.h>
76e737caceSsasano
77e737caceSsasano #define UGL_INTR_PKTLEN 8
78e737caceSsasano #define UGL_BULK_PKTLEN 64
79e737caceSsasano
80e737caceSsasano /***/
81e737caceSsasano
82e737caceSsasano #define UGL_INTR_INTERVAL 20
83e737caceSsasano
84e737caceSsasano #define UGL_MAX_MTU 1514
85e737caceSsasano #define UGL_BUFSZ roundup(sizeof(struct ugl_packet), UGL_BULK_PKTLEN)
86e737caceSsasano
87e737caceSsasano #define UGL_RX_FRAMES 1 /* must be one */
88e737caceSsasano #define UGL_TX_FRAMES 1 /* must be one */
89e737caceSsasano
90e737caceSsasano #define UGL_RX_LIST_CNT 1
91e737caceSsasano #define UGL_TX_LIST_CNT 1
92e737caceSsasano
93e737caceSsasano #define UGL_ENDPT_RX 0x0
94e737caceSsasano #define UGL_ENDPT_TX 0x1
95e737caceSsasano #define UGL_ENDPT_INTR 0x2
96e737caceSsasano #define UGL_ENDPT_MAX 0x3
97e737caceSsasano
98e737caceSsasano struct ugl_softc;
99e737caceSsasano
100e737caceSsasano struct ugl_packet {
101e737caceSsasano uDWord pkt_count;
102e737caceSsasano uDWord pkt_length;
103e737caceSsasano char pkt_data[UGL_MAX_MTU];
104e737caceSsasano } __packed;
105e737caceSsasano
106e737caceSsasano struct ugl_chain {
107e737caceSsasano struct ugl_softc *ugl_sc;
108e737caceSsasano struct usbd_xfer *ugl_xfer;
109e737caceSsasano struct ugl_packet *ugl_buf;
110e737caceSsasano struct mbuf *ugl_mbuf;
111e737caceSsasano int ugl_idx;
112e737caceSsasano };
113e737caceSsasano
114e737caceSsasano struct ugl_cdata {
115e737caceSsasano struct ugl_chain ugl_tx_chain[UGL_TX_LIST_CNT];
116e737caceSsasano struct ugl_chain ugl_rx_chain[UGL_RX_LIST_CNT];
117e737caceSsasano int ugl_tx_prod;
118e737caceSsasano int ugl_tx_cons;
119e737caceSsasano int ugl_tx_cnt;
120e737caceSsasano int ugl_rx_prod;
121e737caceSsasano };
122e737caceSsasano
123e737caceSsasano struct ugl_softc {
124e737caceSsasano struct device sc_dev;
125e737caceSsasano
126e737caceSsasano struct arpcom sc_arpcom;
127e737caceSsasano #define GET_IFP(sc) (&(sc)->sc_arpcom.ac_if)
128e737caceSsasano struct timeout sc_stat_ch;
129e737caceSsasano
130e737caceSsasano struct usbd_device *sc_udev;
131e737caceSsasano struct usbd_interface *sc_iface;
132e737caceSsasano int sc_ed[UGL_ENDPT_MAX];
133e737caceSsasano struct usbd_pipe *sc_ep[UGL_ENDPT_MAX];
134e737caceSsasano struct ugl_cdata sc_cdata;
135e737caceSsasano
136e737caceSsasano uByte sc_ibuf[UGL_INTR_PKTLEN];
137e737caceSsasano
138e737caceSsasano u_int sc_rx_errs;
139e737caceSsasano struct timeval sc_rx_notice;
140e737caceSsasano u_int sc_intr_errs;
141e737caceSsasano };
142e737caceSsasano
143e737caceSsasano #ifdef UGL_DEBUG
144e737caceSsasano #define DPRINTF(x) do { if (ugldebug) printf x; } while (0)
145e737caceSsasano #define DPRINTFN(n,x) do { if (ugldebug >= (n)) printf x; } while (0)
146e737caceSsasano int ugldebug = 0;
147e737caceSsasano #else
148e737caceSsasano #define DPRINTF(x)
149e737caceSsasano #define DPRINTFN(n,x)
150e737caceSsasano #endif
151e737caceSsasano
152e737caceSsasano /*
153e737caceSsasano * Various supported device vendors/products.
154e737caceSsasano */
155e737caceSsasano struct usb_devno ugl_devs[] = {
156e737caceSsasano { USB_VENDOR_GENESYS, USB_PRODUCT_GENESYS_GL620USB_A },
157e737caceSsasano };
158e737caceSsasano
159e737caceSsasano int ugl_match(struct device *, void *, void *);
160e737caceSsasano void ugl_attach(struct device *, struct device *, void *);
161e737caceSsasano int ugl_detach(struct device *, int);
162e737caceSsasano
163e737caceSsasano struct cfdriver ugl_cd = {
164e737caceSsasano NULL, "ugl", DV_IFNET
165e737caceSsasano };
166e737caceSsasano
167e737caceSsasano const struct cfattach ugl_ca = {
16853c6612dSmpi sizeof(struct ugl_softc), ugl_match, ugl_attach, ugl_detach
169e737caceSsasano };
170e737caceSsasano
171e737caceSsasano int ugl_openpipes(struct ugl_softc *);
172e737caceSsasano int ugl_tx_list_init(struct ugl_softc *);
173e737caceSsasano int ugl_rx_list_init(struct ugl_softc *);
174e737caceSsasano int ugl_newbuf(struct ugl_softc *, struct ugl_chain *, struct mbuf *);
175e737caceSsasano int ugl_send(struct ugl_softc *, struct mbuf *, int);
176e737caceSsasano void ugl_intr(struct usbd_xfer *, void *, usbd_status);
177e737caceSsasano void ugl_rxeof(struct usbd_xfer *, void *, usbd_status);
178e737caceSsasano void ugl_txeof(struct usbd_xfer *, void *, usbd_status);
179e737caceSsasano void ugl_start(struct ifnet *);
180e737caceSsasano int ugl_ioctl(struct ifnet *, u_long, caddr_t);
181e737caceSsasano void ugl_init(void *);
182e737caceSsasano void ugl_stop(struct ugl_softc *);
183e737caceSsasano void ugl_watchdog(struct ifnet *);
184e737caceSsasano
185e737caceSsasano /*
186e737caceSsasano * Probe for a Genesys Logic chip.
187e737caceSsasano */
188e737caceSsasano int
ugl_match(struct device * parent,void * match,void * aux)189e737caceSsasano ugl_match(struct device *parent, void *match, void *aux)
190e737caceSsasano {
191e737caceSsasano struct usb_attach_arg *uaa = aux;
192e737caceSsasano
193cca60b12Smpi if (uaa->iface == NULL || uaa->configno != 1)
194e737caceSsasano return (UMATCH_NONE);
195e737caceSsasano
196e737caceSsasano return (usb_lookup(ugl_devs, uaa->vendor, uaa->product) != NULL ?
197cca60b12Smpi UMATCH_VENDOR_PRODUCT_CONF_IFACE : UMATCH_NONE);
198e737caceSsasano }
199e737caceSsasano
200e737caceSsasano void
ugl_attach(struct device * parent,struct device * self,void * aux)201e737caceSsasano ugl_attach(struct device *parent, struct device *self, void *aux)
202e737caceSsasano {
203e737caceSsasano struct ugl_softc *sc = (struct ugl_softc *)self;
204e737caceSsasano struct usb_attach_arg *uaa = aux;
205e737caceSsasano int s;
206e737caceSsasano struct usbd_device *dev = uaa->device;
207cca60b12Smpi struct usbd_interface *iface = uaa->iface;
208777f3d7fSmpi struct ifnet *ifp = GET_IFP(sc);
209e737caceSsasano usb_interface_descriptor_t *id;
210e737caceSsasano usb_endpoint_descriptor_t *ed;
211e737caceSsasano int i;
212e737caceSsasano
213e737caceSsasano DPRINTFN(5,(" : ugl_attach: sc=%p, dev=%p", sc, dev));
214e737caceSsasano
215e737caceSsasano sc->sc_udev = dev;
216e737caceSsasano sc->sc_iface = iface;
217e737caceSsasano id = usbd_get_interface_descriptor(iface);
218e737caceSsasano
219e737caceSsasano /* Find endpoints. */
220e737caceSsasano for (i = 0; i < id->bNumEndpoints; i++) {
221e737caceSsasano ed = usbd_interface2endpoint_descriptor(iface, i);
222e737caceSsasano if (ed == NULL) {
223e737caceSsasano printf("%s: couldn't get ep %d\n",
224e737caceSsasano sc->sc_dev.dv_xname, i);
225e737caceSsasano return;
226e737caceSsasano }
227e737caceSsasano if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
228e737caceSsasano UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) {
229e737caceSsasano sc->sc_ed[UGL_ENDPT_RX] = ed->bEndpointAddress;
230e737caceSsasano } else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT &&
231e737caceSsasano UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) {
232e737caceSsasano sc->sc_ed[UGL_ENDPT_TX] = ed->bEndpointAddress;
233e737caceSsasano } else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
234e737caceSsasano UE_GET_XFERTYPE(ed->bmAttributes) == UE_INTERRUPT) {
235e737caceSsasano sc->sc_ed[UGL_ENDPT_INTR] = ed->bEndpointAddress;
236e737caceSsasano }
237e737caceSsasano }
238e737caceSsasano
239e737caceSsasano if (sc->sc_ed[UGL_ENDPT_RX] == 0 || sc->sc_ed[UGL_ENDPT_TX] == 0 ||
240e737caceSsasano sc->sc_ed[UGL_ENDPT_INTR] == 0) {
241e737caceSsasano printf("%s: missing endpoint\n", sc->sc_dev.dv_xname);
242e737caceSsasano return;
243e737caceSsasano }
244e737caceSsasano
245e737caceSsasano s = splnet();
246e737caceSsasano
247777f3d7fSmpi ether_fakeaddr(ifp);
248e737caceSsasano printf("%s: address %s\n",
249e737caceSsasano sc->sc_dev.dv_xname, ether_sprintf(sc->sc_arpcom.ac_enaddr));
250e737caceSsasano
251e737caceSsasano /* Initialize interface info.*/
252e737caceSsasano ifp->if_softc = sc;
25344fce25bSbrad ifp->if_hardmtu = UGL_MAX_MTU;
254e737caceSsasano ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
255e737caceSsasano ifp->if_ioctl = ugl_ioctl;
256e737caceSsasano ifp->if_start = ugl_start;
257e737caceSsasano ifp->if_watchdog = ugl_watchdog;
258e737caceSsasano strlcpy(ifp->if_xname, sc->sc_dev.dv_xname, IFNAMSIZ);
259e737caceSsasano
260e737caceSsasano /* Attach the interface. */
261e737caceSsasano if_attach(ifp);
262e737caceSsasano ether_ifattach(ifp);
263e737caceSsasano
264e737caceSsasano splx(s);
265e737caceSsasano }
266e737caceSsasano
267e737caceSsasano int
ugl_detach(struct device * self,int flags)268e737caceSsasano ugl_detach(struct device *self, int flags)
269e737caceSsasano {
270e737caceSsasano struct ugl_softc *sc = (struct ugl_softc *)self;
271e737caceSsasano struct ifnet *ifp = GET_IFP(sc);
272e737caceSsasano int s;
273e737caceSsasano
274e737caceSsasano DPRINTFN(2,("%s: %s: enter\n", sc->sc_dev.dv_xname, __func__));
275e737caceSsasano
276e737caceSsasano s = splusb();
277e737caceSsasano
278e737caceSsasano if (ifp->if_flags & IFF_RUNNING)
279e737caceSsasano ugl_stop(sc);
280e737caceSsasano
281e737caceSsasano if (ifp->if_softc != NULL)
282e737caceSsasano if_detach(ifp);
283e737caceSsasano
284e737caceSsasano #ifdef DIAGNOSTIC
285e737caceSsasano if (sc->sc_ep[UGL_ENDPT_TX] != NULL ||
286e737caceSsasano sc->sc_ep[UGL_ENDPT_RX] != NULL ||
287e737caceSsasano sc->sc_ep[UGL_ENDPT_INTR] != NULL)
288e737caceSsasano printf("%s: detach has active endpoints\n",
289e737caceSsasano sc->sc_dev.dv_xname);
290e737caceSsasano #endif
291e737caceSsasano
292e737caceSsasano splx(s);
293e737caceSsasano
294e737caceSsasano return (0);
295e737caceSsasano }
296e737caceSsasano
297e737caceSsasano /*
298e737caceSsasano * Initialize an RX descriptor and attach an MBUF cluster.
299e737caceSsasano */
300e737caceSsasano int
ugl_newbuf(struct ugl_softc * sc,struct ugl_chain * c,struct mbuf * m)301e737caceSsasano ugl_newbuf(struct ugl_softc *sc, struct ugl_chain *c, struct mbuf *m)
302e737caceSsasano {
303e737caceSsasano struct mbuf *m_new = NULL;
304e737caceSsasano
305e737caceSsasano DPRINTFN(8,("%s: %s: enter\n", sc->sc_dev.dv_xname, __func__));
306e737caceSsasano
307e737caceSsasano if (m == NULL) {
308e737caceSsasano MGETHDR(m_new, M_DONTWAIT, MT_DATA);
309e737caceSsasano if (m_new == NULL) {
310e737caceSsasano printf("%s: no memory for rx list "
311e737caceSsasano "-- packet dropped!\n", sc->sc_dev.dv_xname);
312e737caceSsasano return (ENOBUFS);
313e737caceSsasano }
314e737caceSsasano
315e737caceSsasano MCLGET(m_new, M_DONTWAIT);
316e737caceSsasano if (!(m_new->m_flags & M_EXT)) {
317e737caceSsasano printf("%s: no memory for rx list "
318e737caceSsasano "-- packet dropped!\n", sc->sc_dev.dv_xname);
319e737caceSsasano m_freem(m_new);
320e737caceSsasano return (ENOBUFS);
321e737caceSsasano }
322e737caceSsasano m_new->m_len = m_new->m_pkthdr.len = MCLBYTES;
323e737caceSsasano } else {
324e737caceSsasano m_new = m;
325e737caceSsasano m_new->m_len = m_new->m_pkthdr.len = MCLBYTES;
326e737caceSsasano m_new->m_data = m_new->m_ext.ext_buf;
327e737caceSsasano }
328e737caceSsasano
329e737caceSsasano c->ugl_mbuf = m_new;
330e737caceSsasano
331e737caceSsasano return (0);
332e737caceSsasano }
333e737caceSsasano
334e737caceSsasano int
ugl_rx_list_init(struct ugl_softc * sc)335e737caceSsasano ugl_rx_list_init(struct ugl_softc *sc)
336e737caceSsasano {
337e737caceSsasano struct ugl_cdata *cd;
338e737caceSsasano struct ugl_chain *c;
339e737caceSsasano int i;
340e737caceSsasano
341e737caceSsasano DPRINTFN(5,("%s: %s: enter\n", sc->sc_dev.dv_xname, __func__));
342e737caceSsasano
343e737caceSsasano cd = &sc->sc_cdata;
344e737caceSsasano for (i = 0; i < UGL_RX_LIST_CNT; i++) {
345e737caceSsasano c = &cd->ugl_rx_chain[i];
346e737caceSsasano c->ugl_sc = sc;
347e737caceSsasano c->ugl_idx = i;
348e737caceSsasano if (ugl_newbuf(sc, c, NULL) == ENOBUFS)
349e737caceSsasano return (ENOBUFS);
350e737caceSsasano if (c->ugl_xfer == NULL) {
351e737caceSsasano c->ugl_xfer = usbd_alloc_xfer(sc->sc_udev);
352e737caceSsasano if (c->ugl_xfer == NULL)
353e737caceSsasano return (ENOBUFS);
354e737caceSsasano c->ugl_buf = usbd_alloc_buffer(c->ugl_xfer, UGL_BUFSZ);
355e737caceSsasano if (c->ugl_buf == NULL) {
356e737caceSsasano usbd_free_xfer(c->ugl_xfer);
357e737caceSsasano return (ENOBUFS);
358e737caceSsasano }
359e737caceSsasano }
360e737caceSsasano }
361e737caceSsasano
362e737caceSsasano return (0);
363e737caceSsasano }
364e737caceSsasano
365e737caceSsasano int
ugl_tx_list_init(struct ugl_softc * sc)366e737caceSsasano ugl_tx_list_init(struct ugl_softc *sc)
367e737caceSsasano {
368e737caceSsasano struct ugl_cdata *cd;
369e737caceSsasano struct ugl_chain *c;
370e737caceSsasano int i;
371e737caceSsasano
372e737caceSsasano DPRINTFN(5,("%s: %s: enter\n", sc->sc_dev.dv_xname, __func__));
373e737caceSsasano
374e737caceSsasano cd = &sc->sc_cdata;
375e737caceSsasano for (i = 0; i < UGL_TX_LIST_CNT; i++) {
376e737caceSsasano c = &cd->ugl_tx_chain[i];
377e737caceSsasano c->ugl_sc = sc;
378e737caceSsasano c->ugl_idx = i;
379e737caceSsasano c->ugl_mbuf = NULL;
380e737caceSsasano if (c->ugl_xfer == NULL) {
381e737caceSsasano c->ugl_xfer = usbd_alloc_xfer(sc->sc_udev);
382e737caceSsasano if (c->ugl_xfer == NULL)
383e737caceSsasano return (ENOBUFS);
384e737caceSsasano c->ugl_buf = usbd_alloc_buffer(c->ugl_xfer, UGL_BUFSZ);
385e737caceSsasano if (c->ugl_buf == NULL) {
386e737caceSsasano usbd_free_xfer(c->ugl_xfer);
387e737caceSsasano return (ENOBUFS);
388e737caceSsasano }
389e737caceSsasano }
390e737caceSsasano }
391e737caceSsasano
392e737caceSsasano return (0);
393e737caceSsasano }
394e737caceSsasano
395e737caceSsasano /*
396e737caceSsasano * A frame has been uploaded: pass the resulting mbuf chain up to
397e737caceSsasano * the higher level protocols.
398e737caceSsasano */
399e737caceSsasano void
ugl_rxeof(struct usbd_xfer * xfer,void * priv,usbd_status status)400e737caceSsasano ugl_rxeof(struct usbd_xfer *xfer, void *priv, usbd_status status)
401e737caceSsasano {
402e737caceSsasano struct ugl_chain *c = priv;
403e737caceSsasano struct ugl_softc *sc = c->ugl_sc;
404e737caceSsasano struct ifnet *ifp = GET_IFP(sc);
405aa0dd4f6Smpi struct mbuf_list ml = MBUF_LIST_INITIALIZER();
406e737caceSsasano struct mbuf *m;
407e737caceSsasano int total_len = 0;
408e737caceSsasano unsigned int packet_len, packet_count;
409e737caceSsasano int s;
410e737caceSsasano
411e737caceSsasano if (usbd_is_dying(sc->sc_udev))
412e737caceSsasano return;
413e737caceSsasano
414e737caceSsasano if (!(ifp->if_flags & IFF_RUNNING))
415e737caceSsasano return;
416e737caceSsasano
417e737caceSsasano if (status != USBD_NORMAL_COMPLETION) {
418e737caceSsasano if (status == USBD_NOT_STARTED || status == USBD_CANCELLED)
419e737caceSsasano return;
420e737caceSsasano sc->sc_rx_errs++;
421e737caceSsasano if (usbd_ratecheck(&sc->sc_rx_notice)) {
422e737caceSsasano printf("%s: %u usb errors on rx: %s\n",
423e737caceSsasano sc->sc_dev.dv_xname, sc->sc_rx_errs,
424e737caceSsasano usbd_errstr(status));
425e737caceSsasano sc->sc_rx_errs = 0;
426e737caceSsasano }
427e737caceSsasano if (status == USBD_STALLED)
428e737caceSsasano usbd_clear_endpoint_stall_async(sc->sc_ep[UGL_ENDPT_RX]);
429e737caceSsasano goto done;
430e737caceSsasano }
431e737caceSsasano
432e737caceSsasano usbd_get_xfer_status(xfer, NULL, NULL, &total_len, NULL);
433e737caceSsasano
434e737caceSsasano DPRINTFN(9,("%s: %s: enter status=%d length=%d\n",
435e737caceSsasano sc->sc_dev.dv_xname, __func__, status, total_len));
436e737caceSsasano
437e737caceSsasano if (total_len < offsetof(struct ugl_packet, pkt_data)) {
438e737caceSsasano printf("%s: bad header (length=%d)\n",
439e737caceSsasano sc->sc_dev.dv_xname, total_len);
440e737caceSsasano
441e737caceSsasano goto done;
442e737caceSsasano }
443e737caceSsasano
444e737caceSsasano packet_count = UGETDW(c->ugl_buf->pkt_count);
445e737caceSsasano if (packet_count != UGL_RX_FRAMES) {
446e737caceSsasano printf("%s: bad packet count (%d)\n",
447e737caceSsasano sc->sc_dev.dv_xname, packet_count);
448e737caceSsasano
449e737caceSsasano if (packet_count == 0)
450e737caceSsasano goto done;
451e737caceSsasano }
452e737caceSsasano
453e737caceSsasano packet_len = UGETDW(c->ugl_buf->pkt_length);
454e737caceSsasano if (total_len < packet_len) {
455e737caceSsasano printf("%s: bad packet size(%d), length=%d\n",
456e737caceSsasano sc->sc_dev.dv_xname, packet_len, total_len);
457e737caceSsasano
458e737caceSsasano if (packet_len == 0)
459e737caceSsasano goto done;
460e737caceSsasano }
461e737caceSsasano
462e737caceSsasano m = c->ugl_mbuf;
463e737caceSsasano memcpy(mtod(c->ugl_mbuf, char *), c->ugl_buf->pkt_data, packet_len);
464e737caceSsasano
465e737caceSsasano m->m_pkthdr.len = m->m_len = packet_len;
466aa0dd4f6Smpi ml_enqueue(&ml, m);
467e737caceSsasano
468e737caceSsasano if (ugl_newbuf(sc, c, NULL) == ENOBUFS) {
469e737caceSsasano ifp->if_ierrors++;
470aa0dd4f6Smpi goto done;
471e737caceSsasano }
472e737caceSsasano
473aa0dd4f6Smpi s = splnet();
474aa0dd4f6Smpi if_input(ifp, &ml);
475e737caceSsasano splx(s);
476e737caceSsasano
477e737caceSsasano done:
478e737caceSsasano /* Setup new transfer. */
479e737caceSsasano usbd_setup_xfer(c->ugl_xfer, sc->sc_ep[UGL_ENDPT_RX],
480e737caceSsasano c, c->ugl_buf, UGL_BUFSZ, USBD_SHORT_XFER_OK | USBD_NO_COPY,
481e737caceSsasano USBD_NO_TIMEOUT, ugl_rxeof);
482e737caceSsasano usbd_transfer(c->ugl_xfer);
483e737caceSsasano
484e737caceSsasano DPRINTFN(10,("%s: %s: start rx\n", sc->sc_dev.dv_xname,
485e737caceSsasano __func__));
486e737caceSsasano }
487e737caceSsasano
488e737caceSsasano /*
489e737caceSsasano * A frame was downloaded to the chip. It's safe for us to clean up
490e737caceSsasano * the list buffers.
491e737caceSsasano */
492e737caceSsasano void
ugl_txeof(struct usbd_xfer * xfer,void * priv,usbd_status status)493e737caceSsasano ugl_txeof(struct usbd_xfer *xfer, void *priv, usbd_status status)
494e737caceSsasano {
495e737caceSsasano struct ugl_chain *c = priv;
496e737caceSsasano struct ugl_softc *sc = c->ugl_sc;
497e737caceSsasano struct ifnet *ifp = GET_IFP(sc);
498e737caceSsasano int s;
499e737caceSsasano
500e737caceSsasano if (usbd_is_dying(sc->sc_udev))
501e737caceSsasano return;
502e737caceSsasano
503e737caceSsasano s = splnet();
504e737caceSsasano
505e737caceSsasano DPRINTFN(10,("%s: %s: enter status=%d\n", sc->sc_dev.dv_xname,
506e737caceSsasano __func__, status));
507e737caceSsasano
508e737caceSsasano ifp->if_timer = 0;
509de6cd8fbSdlg ifq_clr_oactive(&ifp->if_snd);
510e737caceSsasano
511e737caceSsasano if (status != USBD_NORMAL_COMPLETION) {
512e737caceSsasano if (status == USBD_NOT_STARTED || status == USBD_CANCELLED) {
513e737caceSsasano splx(s);
514e737caceSsasano return;
515e737caceSsasano }
516e737caceSsasano ifp->if_oerrors++;
517e737caceSsasano printf("%s: usb error on tx: %s\n", sc->sc_dev.dv_xname,
518e737caceSsasano usbd_errstr(status));
519e737caceSsasano if (status == USBD_STALLED)
520e737caceSsasano usbd_clear_endpoint_stall_async(sc->sc_ep[UGL_ENDPT_TX]);
521e737caceSsasano splx(s);
522e737caceSsasano return;
523e737caceSsasano }
524e737caceSsasano
525e737caceSsasano m_freem(c->ugl_mbuf);
526e737caceSsasano c->ugl_mbuf = NULL;
527e737caceSsasano
5280cae21bdSpatrick if (ifq_empty(&ifp->if_snd) == 0)
529e737caceSsasano ugl_start(ifp);
530e737caceSsasano
531e737caceSsasano splx(s);
532e737caceSsasano }
533e737caceSsasano
534e737caceSsasano int
ugl_send(struct ugl_softc * sc,struct mbuf * m,int idx)535e737caceSsasano ugl_send(struct ugl_softc *sc, struct mbuf *m, int idx)
536e737caceSsasano {
537e737caceSsasano int total_len;
538e737caceSsasano struct ugl_chain *c;
539e737caceSsasano usbd_status err;
540e737caceSsasano
541e737caceSsasano c = &sc->sc_cdata.ugl_tx_chain[idx];
542e737caceSsasano
543e737caceSsasano /*
544e737caceSsasano * Copy the mbuf data into a contiguous buffer, leaving two
545e737caceSsasano * bytes at the beginning to hold the frame length.
546e737caceSsasano */
547e737caceSsasano USETDW(c->ugl_buf->pkt_count, UGL_TX_FRAMES);
548e737caceSsasano USETDW(c->ugl_buf->pkt_length, m->m_pkthdr.len);
549e737caceSsasano m_copydata(m, 0, m->m_pkthdr.len, c->ugl_buf->pkt_data);
550e737caceSsasano c->ugl_mbuf = m;
551e737caceSsasano
552e737caceSsasano total_len = offsetof(struct ugl_packet, pkt_data[m->m_pkthdr.len]);
553e737caceSsasano
554e737caceSsasano DPRINTFN(10,("%s: %s: total_len=%d\n",
555e737caceSsasano sc->sc_dev.dv_xname, __func__, total_len));
556e737caceSsasano
557e737caceSsasano usbd_setup_xfer(c->ugl_xfer, sc->sc_ep[UGL_ENDPT_TX],
558e737caceSsasano c, c->ugl_buf, total_len, USBD_FORCE_SHORT_XFER | USBD_NO_COPY,
559e737caceSsasano USBD_DEFAULT_TIMEOUT, ugl_txeof);
560e737caceSsasano
561e737caceSsasano /* Transmit */
562e737caceSsasano err = usbd_transfer(c->ugl_xfer);
563e737caceSsasano if (err != USBD_IN_PROGRESS) {
564e737caceSsasano printf("%s: ugl_send error=%s\n", sc->sc_dev.dv_xname,
565e737caceSsasano usbd_errstr(err));
5664ced949bSgerhard c->ugl_mbuf = NULL;
567e737caceSsasano ugl_stop(sc);
568e737caceSsasano return (EIO);
569e737caceSsasano }
570e737caceSsasano
571e737caceSsasano sc->sc_cdata.ugl_tx_cnt++;
572e737caceSsasano
573e737caceSsasano return (0);
574e737caceSsasano }
575e737caceSsasano
576e737caceSsasano void
ugl_start(struct ifnet * ifp)577e737caceSsasano ugl_start(struct ifnet *ifp)
578e737caceSsasano {
579e737caceSsasano struct ugl_softc *sc = ifp->if_softc;
580e737caceSsasano struct mbuf *m_head = NULL;
581e737caceSsasano
582e737caceSsasano if (usbd_is_dying(sc->sc_udev))
583e737caceSsasano return;
584e737caceSsasano
585e737caceSsasano DPRINTFN(10,("%s: %s: enter\n", sc->sc_dev.dv_xname,__func__));
586e737caceSsasano
587de6cd8fbSdlg if (ifq_is_oactive(&ifp->if_snd))
588e737caceSsasano return;
589e737caceSsasano
5904ced949bSgerhard m_head = ifq_dequeue(&ifp->if_snd);
591e737caceSsasano if (m_head == NULL)
592e737caceSsasano return;
593e737caceSsasano
594e737caceSsasano if (ugl_send(sc, m_head, 0)) {
5954ced949bSgerhard m_freem(m_head);
596de6cd8fbSdlg ifq_set_oactive(&ifp->if_snd);
597e737caceSsasano return;
598e737caceSsasano }
599e737caceSsasano
600e737caceSsasano #if NBPFILTER > 0
601e737caceSsasano /*
602e737caceSsasano * If there's a BPF listener, bounce a copy of this frame
603e737caceSsasano * to him.
604e737caceSsasano */
605e737caceSsasano if (ifp->if_bpf)
606e737caceSsasano bpf_mtap(ifp->if_bpf, m_head, BPF_DIRECTION_OUT);
607e737caceSsasano #endif
608e737caceSsasano
609de6cd8fbSdlg ifq_set_oactive(&ifp->if_snd);
610e737caceSsasano
611e737caceSsasano /*
612e737caceSsasano * Set a timeout in case the chip goes out to lunch.
613e737caceSsasano */
614e737caceSsasano ifp->if_timer = 5;
615e737caceSsasano }
616e737caceSsasano
617e737caceSsasano void
ugl_init(void * xsc)618e737caceSsasano ugl_init(void *xsc)
619e737caceSsasano {
620e737caceSsasano struct ugl_softc *sc = xsc;
621e737caceSsasano struct ifnet *ifp = GET_IFP(sc);
622e737caceSsasano int s;
623e737caceSsasano
624e737caceSsasano if (usbd_is_dying(sc->sc_udev))
625e737caceSsasano return;
626e737caceSsasano
627e737caceSsasano DPRINTFN(10,("%s: %s: enter\n", sc->sc_dev.dv_xname,__func__));
628e737caceSsasano
629e737caceSsasano s = splnet();
630e737caceSsasano
631e737caceSsasano /* Init TX ring. */
632e737caceSsasano if (ugl_tx_list_init(sc) == ENOBUFS) {
633e737caceSsasano printf("%s: tx list init failed\n", sc->sc_dev.dv_xname);
634e737caceSsasano splx(s);
635e737caceSsasano return;
636e737caceSsasano }
637e737caceSsasano
638e737caceSsasano /* Init RX ring. */
639e737caceSsasano if (ugl_rx_list_init(sc) == ENOBUFS) {
640e737caceSsasano printf("%s: rx list init failed\n", sc->sc_dev.dv_xname);
641e737caceSsasano splx(s);
642e737caceSsasano return;
643e737caceSsasano }
644e737caceSsasano
645e737caceSsasano if (sc->sc_ep[UGL_ENDPT_RX] == NULL) {
646e737caceSsasano if (ugl_openpipes(sc)) {
647e737caceSsasano splx(s);
648e737caceSsasano return;
649e737caceSsasano }
650e737caceSsasano }
651e737caceSsasano
652e737caceSsasano ifp->if_flags |= IFF_RUNNING;
653de6cd8fbSdlg ifq_clr_oactive(&ifp->if_snd);
6544bedc3dcSuaa
6554bedc3dcSuaa splx(s);
656e737caceSsasano }
657e737caceSsasano
658e737caceSsasano int
ugl_openpipes(struct ugl_softc * sc)659e737caceSsasano ugl_openpipes(struct ugl_softc *sc)
660e737caceSsasano {
661e737caceSsasano struct ugl_chain *c;
662e737caceSsasano usbd_status err;
663e737caceSsasano int i;
664e737caceSsasano
665e737caceSsasano /* Open RX and TX pipes. */
666e737caceSsasano err = usbd_open_pipe(sc->sc_iface, sc->sc_ed[UGL_ENDPT_RX],
667e737caceSsasano USBD_EXCLUSIVE_USE, &sc->sc_ep[UGL_ENDPT_RX]);
668e737caceSsasano if (err) {
669e737caceSsasano printf("%s: open rx pipe failed: %s\n",
670e737caceSsasano sc->sc_dev.dv_xname, usbd_errstr(err));
671e737caceSsasano return (EIO);
672e737caceSsasano }
673e737caceSsasano err = usbd_open_pipe(sc->sc_iface, sc->sc_ed[UGL_ENDPT_TX],
674e737caceSsasano USBD_EXCLUSIVE_USE, &sc->sc_ep[UGL_ENDPT_TX]);
675e737caceSsasano if (err) {
676e737caceSsasano printf("%s: open tx pipe failed: %s\n",
677e737caceSsasano sc->sc_dev.dv_xname, usbd_errstr(err));
678e737caceSsasano return (EIO);
679e737caceSsasano }
680e737caceSsasano err = usbd_open_pipe_intr(sc->sc_iface, sc->sc_ed[UGL_ENDPT_INTR],
6819d477d68Sjmatthew 0, &sc->sc_ep[UGL_ENDPT_INTR], sc,
682e737caceSsasano sc->sc_ibuf, UGL_INTR_PKTLEN, ugl_intr,
683e737caceSsasano UGL_INTR_INTERVAL);
684e737caceSsasano if (err) {
685e737caceSsasano printf("%s: open intr pipe failed: %s\n",
686e737caceSsasano sc->sc_dev.dv_xname, usbd_errstr(err));
687e737caceSsasano return (EIO);
688e737caceSsasano }
689e737caceSsasano
690e737caceSsasano /* Start up the receive pipe. */
691e737caceSsasano for (i = 0; i < UGL_RX_LIST_CNT; i++) {
692e737caceSsasano c = &sc->sc_cdata.ugl_rx_chain[i];
693e737caceSsasano usbd_setup_xfer(c->ugl_xfer, sc->sc_ep[UGL_ENDPT_RX],
694e737caceSsasano c, c->ugl_buf, UGL_BUFSZ,
695e737caceSsasano USBD_SHORT_XFER_OK | USBD_NO_COPY, USBD_NO_TIMEOUT,
696e737caceSsasano ugl_rxeof);
697e737caceSsasano usbd_transfer(c->ugl_xfer);
698e737caceSsasano }
699e737caceSsasano
700e737caceSsasano return (0);
701e737caceSsasano }
702e737caceSsasano
703e737caceSsasano void
ugl_intr(struct usbd_xfer * xfer,void * priv,usbd_status status)704e737caceSsasano ugl_intr(struct usbd_xfer *xfer, void *priv, usbd_status status)
705e737caceSsasano {
706e737caceSsasano struct ugl_softc *sc = priv;
707e737caceSsasano struct ifnet *ifp = GET_IFP(sc);
708e737caceSsasano int i;
709e737caceSsasano
710e737caceSsasano DPRINTFN(15,("%s: %s: enter\n", sc->sc_dev.dv_xname,__func__));
711e737caceSsasano
712e737caceSsasano if (usbd_is_dying(sc->sc_udev))
713e737caceSsasano return;
714e737caceSsasano
715e737caceSsasano if (!(ifp->if_flags & IFF_RUNNING))
716e737caceSsasano return;
717e737caceSsasano
718e737caceSsasano if (status != USBD_NORMAL_COMPLETION) {
719e737caceSsasano if (status == USBD_NOT_STARTED || status == USBD_CANCELLED) {
720e737caceSsasano return;
721e737caceSsasano }
722e737caceSsasano sc->sc_intr_errs++;
723e737caceSsasano if (usbd_ratecheck(&sc->sc_rx_notice)) {
724e737caceSsasano printf("%s: %u usb errors on intr: %s\n",
725e737caceSsasano sc->sc_dev.dv_xname, sc->sc_rx_errs,
726e737caceSsasano usbd_errstr(status));
727e737caceSsasano sc->sc_intr_errs = 0;
728e737caceSsasano }
729e737caceSsasano if (status == USBD_STALLED)
730e737caceSsasano usbd_clear_endpoint_stall_async(sc->sc_ep[UGL_ENDPT_RX]);
731e737caceSsasano return;
732e737caceSsasano }
733e737caceSsasano
734e737caceSsasano DPRINTFN(10,("%s: %s:", sc->sc_dev.dv_xname, __func__));
735e737caceSsasano for (i = 0; i < UGL_INTR_PKTLEN; i++)
736e737caceSsasano DPRINTFN(10,(" 0x%02x", sc->sc_ibuf[i]));
737e737caceSsasano DPRINTFN(10,("\n"));
738e737caceSsasano
739e737caceSsasano }
740e737caceSsasano
741e737caceSsasano int
ugl_ioctl(struct ifnet * ifp,u_long command,caddr_t data)742e737caceSsasano ugl_ioctl(struct ifnet *ifp, u_long command, caddr_t data)
743e737caceSsasano {
744e737caceSsasano struct ugl_softc *sc = ifp->if_softc;
745e737caceSsasano int s, error = 0;
746e737caceSsasano
747e737caceSsasano if (usbd_is_dying(sc->sc_udev))
748947e4cccSstsp return ENXIO;
749e737caceSsasano
750e737caceSsasano DPRINTFN(5,("%s: %s: cmd=0x%08lx\n",
751e737caceSsasano sc->sc_dev.dv_xname, __func__, command));
752e737caceSsasano
753e737caceSsasano s = splnet();
754e737caceSsasano
755e737caceSsasano switch(command) {
756e737caceSsasano case SIOCSIFADDR:
757e737caceSsasano ifp->if_flags |= IFF_UP;
75844fce25bSbrad if (!(ifp->if_flags & IFF_RUNNING))
759e737caceSsasano ugl_init(sc);
760e737caceSsasano break;
761e737caceSsasano
762e737caceSsasano case SIOCSIFFLAGS:
763e737caceSsasano if (ifp->if_flags & IFF_UP) {
76444fce25bSbrad if (ifp->if_flags & IFF_RUNNING)
76544fce25bSbrad error = ENETRESET;
76644fce25bSbrad else
767e737caceSsasano ugl_init(sc);
768e737caceSsasano } else {
769e737caceSsasano if (ifp->if_flags & IFF_RUNNING)
770e737caceSsasano ugl_stop(sc);
771e737caceSsasano }
772e737caceSsasano break;
77344fce25bSbrad
774e737caceSsasano default:
77544fce25bSbrad error = ether_ioctl(ifp, &sc->sc_arpcom, command, data);
776e737caceSsasano break;
777e737caceSsasano }
778e737caceSsasano
77944fce25bSbrad if (error == ENETRESET)
78044fce25bSbrad error = 0;
78144fce25bSbrad
782e737caceSsasano splx(s);
783e737caceSsasano return (error);
784e737caceSsasano }
785e737caceSsasano
786e737caceSsasano void
ugl_watchdog(struct ifnet * ifp)787e737caceSsasano ugl_watchdog(struct ifnet *ifp)
788e737caceSsasano {
789e737caceSsasano struct ugl_softc *sc = ifp->if_softc;
790e737caceSsasano
791e737caceSsasano if (usbd_is_dying(sc->sc_udev))
792e737caceSsasano return;
793e737caceSsasano
794e737caceSsasano ifp->if_oerrors++;
795e737caceSsasano printf("%s: watchdog timeout\n", sc->sc_dev.dv_xname);
796e737caceSsasano }
797e737caceSsasano
798e737caceSsasano /*
799e737caceSsasano * Stop the adapter and free any mbufs allocated to the
800e737caceSsasano * RX and TX lists.
801e737caceSsasano */
802e737caceSsasano void
ugl_stop(struct ugl_softc * sc)803e737caceSsasano ugl_stop(struct ugl_softc *sc)
804e737caceSsasano {
805e737caceSsasano struct ifnet *ifp;
806e737caceSsasano int i;
807e737caceSsasano
808e737caceSsasano DPRINTFN(10,("%s: %s: enter\n", sc->sc_dev.dv_xname,__func__));
809e737caceSsasano
810e737caceSsasano ifp = GET_IFP(sc);
811e737caceSsasano ifp->if_timer = 0;
812de6cd8fbSdlg ifp->if_flags &= ~IFF_RUNNING;
813de6cd8fbSdlg ifq_clr_oactive(&ifp->if_snd);
814e737caceSsasano
815e737caceSsasano /* Stop transfers. */
816e737caceSsasano if (sc->sc_ep[UGL_ENDPT_RX] != NULL) {
817e737caceSsasano usbd_close_pipe(sc->sc_ep[UGL_ENDPT_RX]);
818e737caceSsasano sc->sc_ep[UGL_ENDPT_RX] = NULL;
819e737caceSsasano }
820e737caceSsasano
821e737caceSsasano if (sc->sc_ep[UGL_ENDPT_TX] != NULL) {
822e737caceSsasano usbd_close_pipe(sc->sc_ep[UGL_ENDPT_TX]);
823e737caceSsasano sc->sc_ep[UGL_ENDPT_TX] = NULL;
824e737caceSsasano }
825e737caceSsasano
826e737caceSsasano if (sc->sc_ep[UGL_ENDPT_INTR] != NULL) {
827e737caceSsasano usbd_close_pipe(sc->sc_ep[UGL_ENDPT_INTR]);
828e737caceSsasano sc->sc_ep[UGL_ENDPT_INTR] = NULL;
829e737caceSsasano }
830e737caceSsasano
831e737caceSsasano /* Free RX resources. */
832e737caceSsasano for (i = 0; i < UGL_RX_LIST_CNT; i++) {
833e737caceSsasano if (sc->sc_cdata.ugl_rx_chain[i].ugl_mbuf != NULL) {
834e737caceSsasano m_freem(sc->sc_cdata.ugl_rx_chain[i].ugl_mbuf);
835e737caceSsasano sc->sc_cdata.ugl_rx_chain[i].ugl_mbuf = NULL;
836e737caceSsasano }
837e737caceSsasano if (sc->sc_cdata.ugl_rx_chain[i].ugl_xfer != NULL) {
838e737caceSsasano usbd_free_xfer(sc->sc_cdata.ugl_rx_chain[i].ugl_xfer);
839e737caceSsasano sc->sc_cdata.ugl_rx_chain[i].ugl_xfer = NULL;
840e737caceSsasano }
841e737caceSsasano }
842e737caceSsasano
843e737caceSsasano /* Free TX resources. */
844e737caceSsasano for (i = 0; i < UGL_TX_LIST_CNT; i++) {
845e737caceSsasano if (sc->sc_cdata.ugl_tx_chain[i].ugl_mbuf != NULL) {
846e737caceSsasano m_freem(sc->sc_cdata.ugl_tx_chain[i].ugl_mbuf);
847e737caceSsasano sc->sc_cdata.ugl_tx_chain[i].ugl_mbuf = NULL;
848e737caceSsasano }
849e737caceSsasano if (sc->sc_cdata.ugl_tx_chain[i].ugl_xfer != NULL) {
850e737caceSsasano usbd_free_xfer(sc->sc_cdata.ugl_tx_chain[i].ugl_xfer);
851e737caceSsasano sc->sc_cdata.ugl_tx_chain[i].ugl_xfer = NULL;
852e737caceSsasano }
853e737caceSsasano }
854e737caceSsasano }
855