/*
* Copyright (c) 2019 Matthew R. Green
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/*
* Common code/data shared by all USB ethernet drivers (using these routines.)
*
* This framework provides the following features for USB ethernet drivers:
*
* - USB endpoint pipe handling
* - rx and tx chain handling
* - generic handlers or support for several struct ifnet callbacks
* - MII bus locking
* - interrupt handling
* - partial autoconf handling
*
* Consumers of this interface need to:
*
* - replace most softc members with "struct usbnet" usage, in particular
* use usbnet pointer for ifp->if_softc, and device_private (real softc
* can be stored in un_sc member)
* - use MII bus lock / access methods
* - usbnet_attach() to initialise and allocate rx/tx chains
* - usbnet_attach_ifp() to attach the interface, and either ether_ifattach()
* for ethernet devices, or if_alloc_sadl()/bpf_attach() pair otherwise.
* - usbnet_detach() to clean them up
* - usbnet_activate() for autoconf
* - interface ioctl and start have direct frontends with callbacks for
* device specific handling:
* - ioctl can use either a device-specific override (useful for special
* cases), but provides a normal handler with callback to handle
* ENETRESET conditions that should be sufficient for most users
* - start uses usbnet transmit prepare callback (uno_tx_prepare)
* - interrupt handling:
* - for rx, usbnet will enable the receive pipes and
* call the rx_loop callback to handle device specific processing of
* packets, which can use usbnet_enqueue() to provide data to the
* higher layers
* - for tx, usbnet will pull entries out of the
* transmit queue and use the transmit prepare callback (uno_tx_prepare)
* for the given mbuf. the usb callback will use usbnet_txeof() for
* the transmit completion function (internal to usbnet)
* - there is special interrupt pipe handling
* - timer/tick:
* - the uno_tick callback will be called once a second if present.
*/
/*
* Per-transfer data.
*
* Front-end must set un_rx_list_cnt and un_tx_list_cnt before
* calling usbnet_attach(), and then call usbnet_rx_tx_init()
* which will allocate the chain arrays, and must be NULL to
* indicate the first call.
*/
struct usbnet;
struct usbnet_chain {
struct usbnet *unc_un;
struct usbd_xfer *unc_xfer;
uint8_t *unc_buf;
};
/*
* Extend this as necessary. axe(4) claims to want 6 total, but
* does not implement them.
*/
enum usbnet_ep {
USBNET_ENDPT_RX,
USBNET_ENDPT_TX,
USBNET_ENDPT_INTR,
USBNET_ENDPT_MAX,
};
/* MII read register callback. */
typedef int (*usbnet_mii_read_reg_cb)(struct usbnet *, int reg,
int phy, uint16_t *val);
/* MII write register callback. */
typedef int (*usbnet_mii_write_reg_cb)(struct usbnet *, int reg,
int phy, uint16_t val);
/* MII status change callback. */
typedef void (*usbnet_mii_statchg_cb)(struct ifnet *);
/*
* LOCKING
* =======
*
* The following annotations indicate which locks are held when
* usbnet_ops functions are invoked:
*
* I -> IFNET_LOCK (if_ioctl_lock)
* C -> usbnet un_mcastlock
* M -> usbnet un_miilock
* T -> usbnet un_txlock
* R -> usbnet un_rxlock
* n -> no locks held
*
* Note that the IFNET_LOCK **may not be held** for the ioctl commands
* SIOCADDMULTI/SIOCDELMULTI. These commands are only passed
* explicitly to uno_override_ioctl; for all other devices, they are
* handled by uno_mcast (also without IFNET_LOCK).
*/
struct usbnet_ops {
usbnet_stop_cb uno_stop; /* I */
usbnet_ioctl_cb uno_ioctl; /* I */
usbnet_ioctl_cb uno_override_ioctl; /* I (except mcast) */
usbnet_mcast_cb uno_mcast; /* C */
usbnet_init_cb uno_init; /* I */
usbnet_mii_read_reg_cb uno_read_reg; /* M */
usbnet_mii_write_reg_cb uno_write_reg; /* M */
usbnet_mii_statchg_cb uno_statchg; /* M */
usbnet_tx_prepare_cb uno_tx_prepare; /* T */
usbnet_rx_loop_cb uno_rx_loop; /* R */
usbnet_tick_cb uno_tick; /* n */
usbnet_intr_cb uno_intr; /* n */
};
/*
* USB interrupt pipe support. Use this if usbd_open_pipe_intr() should
* be used for the interrupt pipe.
*/
struct usbnet_intr {
/*
* Point un_intr to this structure to use usbd_open_pipe_intr() not
* usbd_open_pipe() for USBNET_ENDPT_INTR, with this buffer, size,
* and interval.
*/
void *uni_buf;
unsigned uni_bufsz;
unsigned uni_interval;
};
/*
* Structure to setup MII. Use the USBNET_MII_DECL_DEFAULT() macro for
* sane default. Pass a copy to usbnet_attach_ifp(). Not used
* after the usbnet_attach_ifp() function returns.
*/
struct usbnet_mii {
int un_mii_flags;
int un_mii_capmask;
int un_mii_phyloc;
int un_mii_offset;
};
/*
* Generic USB ethernet structure. Use this as ifp->if_softc and set as
* device_private() in attach unless already using struct usbnet here.
*
* Devices without MII should call usbnet_attach_ifp() with have_mii set
* to true, and should ensure that the un_statchg_cb callback sets the
* un_link member. Devices without MII have this forced to true.
*/
struct usbnet_private;
struct usbnet {
/*
* This section should be filled in before calling
* usbnet_attach().
*/
void *un_sc; /* real softc */
device_t un_dev;
struct usbd_interface *un_iface;
struct usbd_device *un_udev;
const struct usbnet_ops *un_ops;
struct usbnet_intr *un_intr;
/* Inputs for rx/tx chain control. */
unsigned un_rx_bufsz;
unsigned un_tx_bufsz;
unsigned un_rx_list_cnt;
unsigned un_tx_list_cnt;
int un_rx_xfer_flags;
int un_tx_xfer_flags;
/*
* This section should be filled in before calling
* usbnet_attach_ifp().
*/
uByte un_ed[USBNET_ENDPT_MAX];
/* MII specific. Not used without MII. */
int un_phyno;
/* Ethernet specific. All zeroes indicates non-Ethernet. */
uint8_t un_eaddr[ETHER_ADDR_LEN];
/*
* This section is for driver to use, not touched by usbnet.
*/
unsigned un_flags;
/*
* This section is private to usbnet. Don't touch.
*/
struct usbnet_private *un_pri;
};