Author: hselasky
Date: Tue Sep 11 22:08:19 2012
New Revision: 240374
URL: http://svn.freebsd.org/changeset/base/240374

Log:
  Fix missing parts of DWC OTG host mode support. The host mode support
  of the DWC OTG is very simple in PIO mode, and we need to re-transmit
  data when NAK is received among other things. We probably will need
  to implement some kind of rate limitation on the NAK-ing.

Modified:
  head/sys/dev/usb/controller/dwc_otg.c
  head/sys/dev/usb/controller/dwc_otg.h

Modified: head/sys/dev/usb/controller/dwc_otg.c
==============================================================================
--- head/sys/dev/usb/controller/dwc_otg.c       Tue Sep 11 21:16:22 2012        
(r240373)
+++ head/sys/dev/usb/controller/dwc_otg.c       Tue Sep 11 22:08:19 2012        
(r240374)
@@ -127,7 +127,6 @@ static dwc_otg_cmd_t dwc_otg_data_tx_syn
 static dwc_otg_cmd_t dwc_otg_host_setup_tx;
 static dwc_otg_cmd_t dwc_otg_host_data_tx;
 static dwc_otg_cmd_t dwc_otg_host_data_rx;
-static dwc_otg_cmd_t dwc_otg_host_data_tx_sync;
 
 static void dwc_otg_device_done(struct usb_xfer *, usb_error_t);
 static void dwc_otg_do_poll(struct usb_bus *);
@@ -505,8 +504,9 @@ dwc_otg_host_channel_alloc(struct dwc_ot
                        /* enable interrupts */
                        DWC_OTG_WRITE_4(sc, DOTG_HCINTMSK(x),
                            HCINT_STALL | HCINT_BBLERR |
-                           HCINT_AHBERR | HCINT_CHHLTD | HCINT_XACTERR |
-                           HCINT_XFERCOMPL);
+                           HCINT_AHBERR | HCINT_CHHLTD |
+                           HCINT_XACTERR | HCINT_XFERCOMPL |
+                           HCINT_NAK | HCINT_NYET);
 
                        DWC_OTG_WRITE_4(sc, DOTG_HCSPLT(x), td->hcsplt);
                        DWC_OTG_WRITE_4(sc, DOTG_HCTSIZ(x), 0);
@@ -544,6 +544,42 @@ dwc_otg_host_setup_tx(struct dwc_otg_td 
        /* get pointer to softc */
        sc = DWC_OTG_PC2SC(td->pc);
 
+       temp = DWC_OTG_READ_4(sc, DOTG_HCINT(td->channel));
+       DWC_OTG_WRITE_4(sc, DOTG_HCINT(td->channel), temp);
+
+       DPRINTF("CH=%d HCINT=0x%08x HCCHAR=0x%08x HCTSIZ=0x%08x\n",
+           td->channel,
+           temp, DWC_OTG_READ_4(sc, DOTG_HCCHAR(td->channel)),
+           DWC_OTG_READ_4(sc, DOTG_HCTSIZ(td->channel)));
+
+       if (temp & HCINT_NAK)
+               td->did_nak = 1;
+
+       if (temp & HCINT_STALL) {
+               td->error_stall = 1;
+               td->error_any = 1;
+               return (0);             /* complete */
+       }
+
+       if (temp & (HCINT_BBLERR |
+           HCINT_AHBERR | HCINT_CHHLTD | HCINT_XACTERR)) {
+               td->error_any = 1;
+               return (0);             /* complete */
+       }
+
+       if (temp & HCINT_XFERCOMPL)
+               td->did_complete = 1;
+
+       if (td->did_complete) {
+               if (td->did_nak == 0) {
+                       td->offset += td->tx_bytes;
+                       td->remainder -= td->tx_bytes;
+                       td->toggle = 1;
+                       return (0);     /* complete */
+               }
+       } else {
+               return (1);     /* busy */
+       }
        temp = DWC_OTG_READ_4(sc, DOTG_HPTXSTS);
 
        DPRINTF("HPTXSTS=0x%08x\n", temp);
@@ -563,9 +599,6 @@ dwc_otg_host_setup_tx(struct dwc_otg_td 
 
        usbd_copy_out(td->pc, 0, &req, sizeof(req));
 
-       td->offset = sizeof(req);
-       td->remainder = 0;
-
        DWC_OTG_WRITE_4(sc, DOTG_HCTSIZ(td->channel),
            (sizeof(req) << HCTSIZ_XFERSIZE_SHIFT) |
            (1 << HCTSIZ_PKTCNT_SHIFT) |
@@ -581,13 +614,12 @@ dwc_otg_host_setup_tx(struct dwc_otg_td 
        bus_space_write_region_4(sc->sc_io_tag, sc->sc_io_hdl,
            DOTG_DFIFO(td->channel), (uint32_t *)&req, sizeof(req) / 4);
 
-       td->toggle = 1;
-
-       /* need to sync before complete */
-       td->func = &dwc_otg_host_data_tx_sync;
+       /* reset statemachine */
+       td->did_complete = 0;
+       td->did_nak = 0;
+       td->tx_bytes = sizeof(req);
 
-       /* check status */
-       return (dwc_otg_host_data_tx_sync(td));
+       return (1);     /* busy */
 }
 
 static uint8_t
@@ -728,13 +760,11 @@ dwc_otg_host_data_rx(struct dwc_otg_td *
        uint32_t temp;
        uint16_t count;
        uint8_t got_short;
-       uint8_t is_isoc;
+       uint8_t ep_type;
 
        if (dwc_otg_host_channel_alloc(td))
                return (1);             /* busy */
 
-       got_short = 0;
-
        /* get pointer to softc */
        sc = DWC_OTG_PC2SC(td->pc);
 
@@ -746,6 +776,9 @@ dwc_otg_host_data_rx(struct dwc_otg_td *
            temp, DWC_OTG_READ_4(sc, DOTG_HCCHAR(td->channel)),
            DWC_OTG_READ_4(sc, DOTG_HCTSIZ(td->channel)));
 
+       if (temp & HCINT_NAK)
+               td->did_nak = 1;
+
        if (temp & HCINT_STALL) {
                td->error_stall = 1;
                td->error_any = 1;
@@ -758,6 +791,9 @@ dwc_otg_host_data_rx(struct dwc_otg_td *
                return (0);             /* complete */
        }
 
+       if (temp & HCINT_XFERCOMPL)
+               td->did_complete = 1;
+
        /* check endpoint status */
        if (sc->sc_last_rx_status == 0)
                goto not_complete;
@@ -770,92 +806,74 @@ dwc_otg_host_data_rx(struct dwc_otg_td *
 
                DPRINTF("DATA\n");
 
-               td->npkt = 0;
+               td->toggle ^= 1;
 
-               if ((sc->sc_last_rx_status &
-                   GRXSTSRD_DPID_MASK) == GRXSTSRD_DPID_DATA0) {
-                       if (td->toggle == 1) {
-                               /* release FIFO - wrong toggle */
-                               DPRINTF("Wrong DT\n");
-                               dwc_otg_common_rx_ack(sc);
-                               goto not_complete;
-                       }
-                       td->toggle = 1;
-               } else {
-                       if (td->toggle == 0) {
-                               /* release FIFO - wrong toggle */
-                               DPRINTF("Wrong DT\n");
+               got_short = 0;
+
+               /* get the packet byte count */
+               count = GRXSTSRD_BCNT_GET(sc->sc_last_rx_status);
+
+               /* verify the packet byte count */
+               if (count != td->max_packet_size) {
+                       if (count < td->max_packet_size) {
+                               /* we have a short packet */
+                               td->short_pkt = 1;
+                               got_short = 1;
+                       } else {
+                               /* invalid USB packet */
+                               td->error_any = 1;
+                         
+                               /* release FIFO */
                                dwc_otg_common_rx_ack(sc);
-                               goto not_complete;
+                               return (0);     /* we are complete */
                        }
-                       td->toggle = 0;
                }
-               break;
-
-       default:
-               DPRINTF("OTHER\n");
-
-               /* release FIFO */
-               dwc_otg_common_rx_ack(sc);
-               goto not_complete;
-       }
-
-       /* get the packet byte count */
-       count = GRXSTSRD_BCNT_GET(sc->sc_last_rx_status);
 
-       /* verify the packet byte count */
-       if (count != td->max_packet_size) {
-               if (count < td->max_packet_size) {
-                       /* we have a short packet */
-                       td->short_pkt = 1;
-                       got_short = 1;
-               } else {
+               /* verify the packet byte count */
+               if (count > td->remainder) {
                        /* invalid USB packet */
                        td->error_any = 1;
 
                        /* release FIFO */
                        dwc_otg_common_rx_ack(sc);
-                       return (0);     /* we are complete */
+                       return (0);             /* we are complete */
                }
-       }
 
-       /* verify the packet byte count */
-       if (count > td->remainder) {
-               /* invalid USB packet */
-               td->error_any = 1;
+               usbd_copy_in(td->pc, td->offset, sc->sc_rx_bounce_buffer, 
count);
+               td->remainder -= count;
+               td->offset += count;
 
                /* release FIFO */
                dwc_otg_common_rx_ack(sc);
-               return (0);             /* we are complete */
-       }
 
-       usbd_copy_in(td->pc, td->offset, sc->sc_rx_bounce_buffer, count);
-       td->remainder -= count;
-       td->offset += count;
+               /* check if we are complete */
+               if ((td->remainder == 0) || got_short) {
+                       if (td->short_pkt) {
+                               /* we are complete */
+                               return (0);
+                       }
+                       /* else need to receive a zero length packet */
+               }
+               break;
 
-       /* release FIFO */
-       dwc_otg_common_rx_ack(sc);
+       default:
+               DPRINTF("OTHER\n");
 
-       /* check if we are complete */
-       if ((td->remainder == 0) || got_short) {
-               if (td->short_pkt) {
-                       /* we are complete */
-                       return (0);
-               }
-               /* else need to receive a zero length packet */
+               /* release FIFO */
+               dwc_otg_common_rx_ack(sc);
+               break;
        }
 
 not_complete:
-
-       if (td->npkt != 0)
+       if (td->did_complete == 0 && td->did_nak == 0)
                return (1);     /* busy */
 
        temp = sc->sc_hcchar[td->channel];
 
-       is_isoc = (((temp & HCCHAR_EPTYPE_MASK) >>
-           HCCHAR_EPTYPE_SHIFT) == UE_ISOCHRONOUS);
+       ep_type = ((temp & HCCHAR_EPTYPE_MASK) >>
+           HCCHAR_EPTYPE_SHIFT);
 
-       if (is_isoc != 0) {
+       if (ep_type == UE_ISOCHRONOUS) {
                if ((sc->sc_sof_val & 0xFF) != td->sof_val)
                        return (1);     /* busy */
                if (td->sof_val & 1)
@@ -863,17 +881,19 @@ not_complete:
                td->sof_val += td->sof_res;
                /* DATA 0 */
                td->toggle = 0;
+       } else if (ep_type == UE_INTERRUPT) {
+               if ((sc->sc_interrupt_val & 0xFF) != td->sof_val)
+                       return (1);     /* busy */
+               td->sof_val += td->sof_res;
        } else if (td->set_toggle) {
                td->set_toggle = 0;
                td->toggle = 1;
        }
 
-       /* receive one packet at a time */
-       td->npkt = 1;
-
+       /* receive one packet */
        DWC_OTG_WRITE_4(sc, DOTG_HCTSIZ(td->channel),
            (td->max_packet_size << HCTSIZ_XFERSIZE_SHIFT) |
-           (td->npkt << HCTSIZ_PKTCNT_SHIFT) |
+           (1 << HCTSIZ_PKTCNT_SHIFT) |
            (td->toggle ? (HCTSIZ_PID_DATA1 << HCTSIZ_PID_SHIFT) :
            (HCTSIZ_PID_DATA0 << HCTSIZ_PID_SHIFT)));
 
@@ -882,6 +902,10 @@ not_complete:
        /* must enable channel before data can be received */
        DWC_OTG_WRITE_4(sc, DOTG_HCCHAR(td->channel), temp);
 
+       /* reset statemachine */
+       td->did_complete = 0;
+       td->did_nak = 0;
+
        return (1);                     /* not complete */
 }
 
@@ -1004,9 +1028,8 @@ dwc_otg_host_data_tx(struct dwc_otg_td *
        struct dwc_otg_softc *sc;
        uint32_t max_buffer;
        uint32_t count;
-       uint32_t mpkt;
        uint32_t temp;
-       uint8_t is_isoc;
+       uint8_t ep_type;
        uint8_t max_frames;
 
        if (dwc_otg_host_channel_alloc(td))
@@ -1023,6 +1046,9 @@ dwc_otg_host_data_tx(struct dwc_otg_td *
            temp, DWC_OTG_READ_4(sc, DOTG_HCCHAR(td->channel)),
            DWC_OTG_READ_4(sc, DOTG_HCTSIZ(td->channel)));
 
+       if (temp & HCINT_NAK)
+               td->did_nak = 1;
+
        if (temp & HCINT_STALL) {
                td->error_stall = 1;
                td->error_any = 1;
@@ -1035,12 +1061,33 @@ dwc_otg_host_data_tx(struct dwc_otg_td *
                return (0);             /* complete */
        }
 
+       if (temp & HCINT_XFERCOMPL)
+               td->did_complete = 1;
+
+       if (td->did_complete) {
+               if (td->did_nak == 0) {
+                       td->offset += td->tx_bytes;
+                       td->remainder -= td->tx_bytes;
+                       td->toggle ^= 1;
+                       td->did_nak = 1;
+
+                       /* check remainder */
+                       if (td->remainder == 0) {
+                               if (td->short_pkt)
+                                       return (0);     /* complete */
+
+                               /* else we need to transmit a short packet */
+                       }
+               }
+       } else {
+               return (1);     /* busy */
+       }
        temp = sc->sc_hcchar[td->channel];
 
-       is_isoc = (((temp & HCCHAR_EPTYPE_MASK) >>
-           HCCHAR_EPTYPE_SHIFT) == UE_ISOCHRONOUS);
+       ep_type = ((temp & HCCHAR_EPTYPE_MASK) >>
+           HCCHAR_EPTYPE_SHIFT);
 
-       if (is_isoc != 0) {
+       if (ep_type == UE_ISOCHRONOUS) {
                if ((sc->sc_sof_val & 0xFF) != td->sof_val)
                        return (1);     /* busy */
                if (td->sof_val & 1)
@@ -1050,20 +1097,15 @@ dwc_otg_host_data_tx(struct dwc_otg_td *
                td->sof_val += td->sof_res;
                td->toggle = 0;
 
+       } else if (ep_type == UE_INTERRUPT) {
+               if ((sc->sc_interrupt_val & 0xFF) != td->sof_val)
+                       return (1);     /* busy */
+               td->sof_val += td->sof_res;
        } else if (td->set_toggle) {
                td->set_toggle = 0;
                td->toggle = 1;
        }
 
-       /* check if all packets have been transferred */
-       temp = DWC_OTG_READ_4(sc, DOTG_HCTSIZ(td->channel));
-       if (temp & HCTSIZ_PKTCNT_MASK) {
-               DPRINTFN(5, "busy ch=%d npkt=%d HCTSIZ=0x%08x\n",
-                   td->channel, (temp & HCTSIZ_PKTCNT_MASK) >>
-                   HCTSIZ_PKTCNT_SHIFT, temp);
-               return (1);     /* busy */
-       }
-
        temp = DWC_OTG_READ_4(sc, DOTG_HPTXSTS);
 
        max_buffer = 4 * (temp & HPTXSTS_PTXFSPCAVAIL_MASK);
@@ -1071,48 +1113,25 @@ dwc_otg_host_data_tx(struct dwc_otg_td *
            >> HPTXSTS_PTXQSPCAVAIL_SHIFT;
 
        max_buffer = max_buffer - (max_buffer % td->max_packet_size);
-       if (max_buffer == 0 || max_frames < 2)
+       if (max_buffer == 0 || max_frames == 0)
                return (1);     /* busy */
 
-       /* try to optimise by sending more data */
-       if (td->channel != 0 &&
-           (td->max_packet_size & 3) == 0 && is_isoc == 0) {
-
+       /* send one packet at a time */
+       count = td->max_packet_size;
+       if (td->remainder < count) {
+               /* we have a short packet */
+               td->short_pkt = 1;
                count = td->remainder;
-               if (count > max_buffer)
-                       count = max_buffer;
-
-               mpkt = count / td->max_packet_size;
-
-               if (mpkt > max_frames) {
-                       mpkt = max_frames;
-                       count = td->max_packet_size * mpkt;
-               } else if ((count == 0) || (count % td->max_packet_size)) {
-                       /* we are transmitting a short packet */
-                       mpkt++;
-                       td->short_pkt = 1;
-               }
-       } else {
-               /* send one packet at a time */
-               mpkt = 1;
-               count = td->max_packet_size;
-               if (td->remainder < count) {
-                       /* we have a short packet */
-                       td->short_pkt = 1;
-                       count = td->remainder;
-               }
        }
 
+       /* TODO: HCTSIZ_DOPNG */
+
        DWC_OTG_WRITE_4(sc, DOTG_HCTSIZ(td->channel),
            (count << HCTSIZ_XFERSIZE_SHIFT) |
-           (mpkt << HCTSIZ_PKTCNT_SHIFT) |
+           (1 << HCTSIZ_PKTCNT_SHIFT) |
            (td->toggle ? (HCTSIZ_PID_DATA1 << HCTSIZ_PID_SHIFT) :
            (HCTSIZ_PID_DATA0 << HCTSIZ_PID_SHIFT)));
 
-       td->toggle ^= (mpkt & 1);
-
-       /* TODO: HCTSIZ_DOPNG */
-
        temp = sc->sc_hcchar[td->channel];
        temp &= ~HCCHAR_EPDIR_IN;
 
@@ -1132,19 +1151,14 @@ dwc_otg_host_data_tx(struct dwc_otg_td *
                bus_space_write_region_4(sc->sc_io_tag, sc->sc_io_hdl,
                    DOTG_DFIFO(td->channel),
                    sc->sc_tx_bounce_buffer, (count + 3) / 4);
-
-               td->remainder -= count;
-               td->offset += count;
        }
 
-       /* check remainder */
-       if (td->remainder == 0) {
-               if (td->short_pkt)
-                       return (0);     /* complete */
+       /* reset statemachine */
+       td->did_complete = 0;
+       td->did_nak = 0;
+       td->tx_bytes = count;
 
-               /* else we need to transmit a short packet */
-       }
-       return (1);                     /* not complete */
+       return (1);     /* busy */
 }
 
 static uint8_t
@@ -1340,44 +1354,6 @@ not_complete:
 }
 
 static uint8_t
-dwc_otg_host_data_tx_sync(struct dwc_otg_td *td)
-{
-       struct dwc_otg_softc *sc;
-       uint32_t temp;
-
-       if (dwc_otg_host_channel_alloc(td))
-               return (1);             /* busy */
-
-       /* get pointer to softc */
-       sc = DWC_OTG_PC2SC(td->pc);
-
-       temp = DWC_OTG_READ_4(sc, DOTG_HCINT(td->channel));
-       DWC_OTG_WRITE_4(sc, DOTG_HCINT(td->channel), temp);
-
-       DPRINTF("CH=%d HCINT=0x%08x HCCHAR=0x%08x HCTSIZ=0x%08x\n",
-           td->channel,
-           temp, DWC_OTG_READ_4(sc, DOTG_HCCHAR(td->channel)),
-           DWC_OTG_READ_4(sc, DOTG_HCTSIZ(td->channel)));
-
-       if (temp & HCINT_STALL) {
-               td->error_stall = 1;
-               td->error_any = 1;
-               return (0);             /* complete */
-       }
-
-       if (temp & (HCINT_BBLERR |
-           HCINT_AHBERR | HCINT_CHHLTD | HCINT_XACTERR)) {
-               td->error_any = 1;
-               return (0);             /* complete */
-       }
-
-       if (temp & HCINT_XFERCOMPL)
-               return (0);             /* complete */
-
-       return (1);     /* busy */
-}
-
-static uint8_t
 dwc_otg_data_tx_sync(struct dwc_otg_td *td)
 {
        struct dwc_otg_softc *sc;
@@ -1842,6 +1818,8 @@ dwc_otg_setup_standard_chain_sub(struct 
        td->error_any = 0;
        td->npkt = 0;
        td->did_stall = temp->did_stall;
+       td->did_nak = 1;
+       td->did_complete = 1;
        td->short_pkt = temp->short_pkt;
        td->alt_next = temp->setup_alt_next;
        td->set_toggle = 0;
@@ -1918,7 +1896,7 @@ dwc_otg_setup_standard_chain(struct usb_
                } else {
                        if (is_host) {
                                temp.func = &dwc_otg_host_data_tx;
-                               need_sync = 1;
+                               need_sync = 0;
                        } else {
                                temp.func = &dwc_otg_data_rx;
                                need_sync = 0;
@@ -1980,13 +1958,8 @@ dwc_otg_setup_standard_chain(struct usb_
 
                /* check if we need to sync */
                if (need_sync) {
-                       if (is_host) {
-                               /* we need a SYNC point after TX */
-                               temp.func = &dwc_otg_host_data_tx_sync;
-                       } else {
-                               /* we need a SYNC point after TX */
-                               temp.func = &dwc_otg_data_tx_sync;
-                       }
+                       /* we need a SYNC point after TX */
+                       temp.func = &dwc_otg_data_tx_sync;
                        dwc_otg_setup_standard_chain_sub(&temp);
                }
 
@@ -2000,7 +1973,7 @@ dwc_otg_setup_standard_chain(struct usb_
                        if (xfer->endpointno & UE_DIR_IN) {
                                if (is_host) {
                                        temp.func = &dwc_otg_host_data_tx;
-                                       need_sync = 1;
+                                       need_sync = 0;
                                } else {
                                        temp.func = &dwc_otg_data_rx;
                                        need_sync = 0;
@@ -2023,10 +1996,7 @@ dwc_otg_setup_standard_chain(struct usb_
 
                        if (need_sync) {
                                /* we need a SYNC point after TX */
-                               if (is_host)
-                                       temp.func = &dwc_otg_host_data_tx_sync;
-                               else
-                                       temp.func = &dwc_otg_data_tx_sync;
+                               temp.func = &dwc_otg_data_tx_sync;
                                dwc_otg_setup_standard_chain_sub(&temp);
                        }
                }
@@ -2040,10 +2010,7 @@ dwc_otg_setup_standard_chain(struct usb_
                        temp.setup_alt_next = 0;
 
                        /* we need a SYNC point after TX */
-                       if (is_host)
-                               temp.func = &dwc_otg_host_data_tx_sync;
-                       else
-                               temp.func = &dwc_otg_data_tx_sync;
+                       temp.func = &dwc_otg_data_tx_sync;
                        dwc_otg_setup_standard_chain_sub(&temp);
                }
        }
@@ -2105,8 +2072,13 @@ dwc_otg_setup_standard_chain(struct usb_
                        break;
                }
 
-               td->sof_val = xfer->endpoint->isoc_next & 0xFF;
-               td->sof_res = 1 << usbd_xfer_get_fps_shift(xfer);
+               if (xfer_type == UE_ISOCHRONOUS) {
+                       td->sof_val = xfer->endpoint->isoc_next & 0xFF;
+                       td->sof_res = 1 << usbd_xfer_get_fps_shift(xfer);
+               } else if (xfer_type == UE_INTERRUPT) {
+                       td->sof_val = sc->sc_interrupt_val;
+                       td->sof_res = 0;        /* TODO */
+               }
        }
 }
 

Modified: head/sys/dev/usb/controller/dwc_otg.h
==============================================================================
--- head/sys/dev/usb/controller/dwc_otg.h       Tue Sep 11 21:16:22 2012        
(r240373)
+++ head/sys/dev/usb/controller/dwc_otg.h       Tue Sep 11 22:08:19 2012        
(r240374)
@@ -66,9 +66,10 @@ struct dwc_otg_td {
        uint8_t alt_next:1;
        uint8_t short_pkt:1;
        uint8_t did_stall:1;
-       uint8_t did_ping:1;
        uint8_t toggle:1;
        uint8_t set_toggle:1;
+       uint8_t did_nak:1;
+       uint8_t did_complete:1;
 };
 
 struct dwc_otg_std_temp {
@@ -150,6 +151,8 @@ struct dwc_otg_softc {
        uint32_t sc_hcchar[DWC_OTG_MAX_CHANNELS];
        uint32_t sc_sof_refs;
        uint32_t sc_sof_val;
+       uint32_t sc_interrupt_refs;
+       uint32_t sc_interrupt_val;
        uint32_t sc_hprt_val;
 
        uint16_t sc_active_rx_ep;
_______________________________________________
svn-src-all@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to