1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
|
/* $NetBSD: usbnet.h,v 1.35 2022/08/22 08:37:16 riastradh Exp $ */
/*
* 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.
*/
#ifndef _DEV_USB_USBNET_H
#define _DEV_USB_USBNET_H
/*
* 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.
*/
#include <sys/device.h>
#include <sys/mbuf.h>
#include <sys/rndsource.h>
#include <sys/mutex.h>
#include <sys/module.h>
#include <net/bpf.h>
#include <net/if.h>
#include <net/if_arp.h>
#include <net/if_dl.h>
#include <net/if_ether.h>
#include <net/if_media.h>
#include <dev/mii/mii.h>
#include <dev/mii/miivar.h>
#include <dev/usb/usb.h>
#include <dev/usb/usbdi.h>
#include <dev/usb/usbdivar.h>
#include <dev/usb/usbdi_util.h>
#include <dev/usb/usbdevs.h>
/*
* 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,
};
/* Interface stop callback. */
typedef void (*usbnet_stop_cb)(struct ifnet *, int);
/* Interface ioctl callback. */
typedef int (*usbnet_ioctl_cb)(struct ifnet *, u_long, void *);
/* Reprogram multicast filters callback. */
typedef void (*usbnet_mcast_cb)(struct ifnet *);
/* Initialise device callback. */
typedef int (*usbnet_init_cb)(struct ifnet *);
/* 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 *);
/* Prepare packet to send callback, returns length. */
typedef unsigned (*usbnet_tx_prepare_cb)(struct usbnet *, struct mbuf *,
struct usbnet_chain *);
/* Receive some packets callback. */
typedef void (*usbnet_rx_loop_cb)(struct usbnet *, struct usbnet_chain *,
uint32_t);
/* Tick callback. */
typedef void (*usbnet_tick_cb)(struct usbnet *);
/* Interrupt pipe callback. */
typedef void (*usbnet_intr_cb)(struct usbnet *, usbd_status);
/*
* 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;
};
#define USBNET_MII_DECL(name, capmask, loc, off, flags) \
struct usbnet_mii name = { \
.un_mii_capmask = capmask, \
.un_mii_phyloc = loc, \
.un_mii_offset = off, \
.un_mii_flags = flags, \
}
#define USBNET_MII_DECL_DEFAULT(name) \
USBNET_MII_DECL(name, 0xffffffff, MII_PHY_ANY, MII_OFFSET_ANY, 0)
/*
* 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;
};
/* Various accessors. */
void usbnet_set_link(struct usbnet *, bool);
struct ifnet *usbnet_ifp(struct usbnet *);
struct ethercom *usbnet_ec(struct usbnet *);
struct mii_data *usbnet_mii(struct usbnet *);
krndsource_t *usbnet_rndsrc(struct usbnet *);
void *usbnet_softc(struct usbnet *);
bool usbnet_havelink(struct usbnet *);
bool usbnet_isdying(struct usbnet *);
bool usbnet_ispromisc(struct usbnet *);
/*
* Endpoint / rx/tx chain management:
*
* 1. usbnet_attach() initialises usbnet and allocates rx and tx chains
*
* 2. On if_init, usbnet:
* - calls uno_init to initialize hardware
* - open pipes
* - initialises the rx/tx chains for use
* - calls uno_mcast to program hardware multicast filter
*
* 3. On if_stop, usbnet:
* - stops pipes
* - calls uno_stop to stop hardware (unless we're detaching anyway)
* - cleans (not frees) rx/tx chains
* - closes pipes
*
* usbnet_detach() frees the rx/tx chains
*
* Setup un_ed[] with valid end points before calling usbnet_attach().
*/
/* interrupt handling */
void usbnet_enqueue(struct usbnet * const, uint8_t *, size_t, int,
uint32_t, int);
void usbnet_input(struct usbnet * const, uint8_t *, size_t);
/* autoconf */
void usbnet_attach(struct usbnet *);
void usbnet_attach_ifp(struct usbnet *, unsigned, unsigned,
const struct usbnet_mii *);
int usbnet_detach(device_t, int);
int usbnet_activate(device_t, devact_t);
/* module hook up */
#ifdef _MODULE
#define USBNET_INIT(name) \
error = config_init_component(cfdriver_ioconf_##name, \
cfattach_ioconf_##name, cfdata_ioconf_##name);
#define USBNET_FINI(name) \
error = config_fini_component(cfdriver_ioconf_##name, \
cfattach_ioconf_##name, cfdata_ioconf_##name);
#else
#define USBNET_INIT(name)
#define USBNET_FINI(name)
#endif
#define USBNET_MODULE(name) \
\
MODULE(MODULE_CLASS_DRIVER, if_##name, "usbnet"); \
\
static int \
if_##name##_modcmd(modcmd_t cmd, void *aux) \
{ \
int error = 0; \
\
switch (cmd) { \
case MODULE_CMD_INIT: \
USBNET_INIT(name) \
return error; \
case MODULE_CMD_FINI: \
USBNET_FINI(name) \
return error; \
default: \
return ENOTTY; \
} \
}
#endif /* _DEV_USB_USBNET_H */
|