|  | @@ -21,8 +21,11 @@ FILE_LICENCE ( GPL2_OR_LATER );
 | 
		
	
		
			
			| 21 | 21 |  
 | 
		
	
		
			
			| 22 | 22 |  #include <stdint.h>
 | 
		
	
		
			
			| 23 | 23 |  #include <errno.h>
 | 
		
	
		
			
			|  | 24 | +#include <ipxe/netdevice.h>
 | 
		
	
		
			
			|  | 25 | +#include <ipxe/ethernet.h>
 | 
		
	
		
			
			| 24 | 26 |  #include <ipxe/if_ether.h>
 | 
		
	
		
			
			| 25 | 27 |  #include <ipxe/base16.h>
 | 
		
	
		
			
			|  | 28 | +#include <ipxe/profile.h>
 | 
		
	
		
			
			| 26 | 29 |  #include <ipxe/usb.h>
 | 
		
	
		
			
			| 27 | 30 |  #include "ecm.h"
 | 
		
	
		
			
			| 28 | 31 |  
 | 
		
	
	
		
			
			|  | @@ -32,6 +35,29 @@ FILE_LICENCE ( GPL2_OR_LATER );
 | 
		
	
		
			
			| 32 | 35 |   *
 | 
		
	
		
			
			| 33 | 36 |   */
 | 
		
	
		
			
			| 34 | 37 |  
 | 
		
	
		
			
			|  | 38 | +/** Refill profiler */
 | 
		
	
		
			
			|  | 39 | +static struct profiler ecm_refill_profiler __profiler =
 | 
		
	
		
			
			|  | 40 | +	{ .name = "ecm.refill" };
 | 
		
	
		
			
			|  | 41 | +
 | 
		
	
		
			
			|  | 42 | +/** Interrupt completion profiler */
 | 
		
	
		
			
			|  | 43 | +static struct profiler ecm_intr_profiler __profiler =
 | 
		
	
		
			
			|  | 44 | +	{ .name = "ecm.intr" };
 | 
		
	
		
			
			|  | 45 | +
 | 
		
	
		
			
			|  | 46 | +/** Bulk IN completion profiler */
 | 
		
	
		
			
			|  | 47 | +static struct profiler ecm_in_profiler __profiler =
 | 
		
	
		
			
			|  | 48 | +	{ .name = "ecm.in" };
 | 
		
	
		
			
			|  | 49 | +
 | 
		
	
		
			
			|  | 50 | +/** Bulk OUT profiler */
 | 
		
	
		
			
			|  | 51 | +static struct profiler ecm_out_profiler __profiler =
 | 
		
	
		
			
			|  | 52 | +	{ .name = "ecm.out" };
 | 
		
	
		
			
			|  | 53 | +
 | 
		
	
		
			
			|  | 54 | +/******************************************************************************
 | 
		
	
		
			
			|  | 55 | + *
 | 
		
	
		
			
			|  | 56 | + * Ethernet functional descriptor
 | 
		
	
		
			
			|  | 57 | + *
 | 
		
	
		
			
			|  | 58 | + ******************************************************************************
 | 
		
	
		
			
			|  | 59 | + */
 | 
		
	
		
			
			|  | 60 | +
 | 
		
	
		
			
			| 35 | 61 |  /**
 | 
		
	
		
			
			| 36 | 62 |   * Locate Ethernet functional descriptor
 | 
		
	
		
			
			| 37 | 63 |   *
 | 
		
	
	
		
			
			|  | @@ -87,3 +113,634 @@ int ecm_fetch_mac ( struct usb_device *usb,
 | 
		
	
		
			
			| 87 | 113 |  
 | 
		
	
		
			
			| 88 | 114 |  	return 0;
 | 
		
	
		
			
			| 89 | 115 |  }
 | 
		
	
		
			
			|  | 116 | +
 | 
		
	
		
			
			|  | 117 | +/******************************************************************************
 | 
		
	
		
			
			|  | 118 | + *
 | 
		
	
		
			
			|  | 119 | + * Ring management
 | 
		
	
		
			
			|  | 120 | + *
 | 
		
	
		
			
			|  | 121 | + ******************************************************************************
 | 
		
	
		
			
			|  | 122 | + */
 | 
		
	
		
			
			|  | 123 | +
 | 
		
	
		
			
			|  | 124 | +/**
 | 
		
	
		
			
			|  | 125 | + * Transcribe receive ring name (for debugging)
 | 
		
	
		
			
			|  | 126 | + *
 | 
		
	
		
			
			|  | 127 | + * @v ecm		CDC-ECM device
 | 
		
	
		
			
			|  | 128 | + * @v ring		Receive ring
 | 
		
	
		
			
			|  | 129 | + * @ret name		Receive ring name
 | 
		
	
		
			
			|  | 130 | + */
 | 
		
	
		
			
			|  | 131 | +static inline const char * ecm_rx_name ( struct ecm_device *ecm,
 | 
		
	
		
			
			|  | 132 | +					 struct ecm_rx_ring *ring ) {
 | 
		
	
		
			
			|  | 133 | +	if ( ring == &ecm->intr ) {
 | 
		
	
		
			
			|  | 134 | +		return "interrupt";
 | 
		
	
		
			
			|  | 135 | +	} else if ( ring == &ecm->in ) {
 | 
		
	
		
			
			|  | 136 | +		return "bulk IN";
 | 
		
	
		
			
			|  | 137 | +	} else {
 | 
		
	
		
			
			|  | 138 | +		return "UNKNOWN";
 | 
		
	
		
			
			|  | 139 | +	}
 | 
		
	
		
			
			|  | 140 | +}
 | 
		
	
		
			
			|  | 141 | +
 | 
		
	
		
			
			|  | 142 | +/**
 | 
		
	
		
			
			|  | 143 | + * Refill receive ring
 | 
		
	
		
			
			|  | 144 | + *
 | 
		
	
		
			
			|  | 145 | + * @v ecm		CDC-ECM device
 | 
		
	
		
			
			|  | 146 | + * @v ring		Receive ring
 | 
		
	
		
			
			|  | 147 | + */
 | 
		
	
		
			
			|  | 148 | +static void ecm_rx_refill ( struct ecm_device *ecm, struct ecm_rx_ring *ring ) {
 | 
		
	
		
			
			|  | 149 | +	struct net_device *netdev = ecm->netdev;
 | 
		
	
		
			
			|  | 150 | +	struct io_buffer *iobuf;
 | 
		
	
		
			
			|  | 151 | +	int rc;
 | 
		
	
		
			
			|  | 152 | +
 | 
		
	
		
			
			|  | 153 | +	/* Refill ring */
 | 
		
	
		
			
			|  | 154 | +	while ( ring->fill < ring->max ) {
 | 
		
	
		
			
			|  | 155 | +
 | 
		
	
		
			
			|  | 156 | +		/* Profile refill */
 | 
		
	
		
			
			|  | 157 | +		profile_start ( &ecm_refill_profiler );
 | 
		
	
		
			
			|  | 158 | +
 | 
		
	
		
			
			|  | 159 | +		/* Allocate I/O buffer */
 | 
		
	
		
			
			|  | 160 | +		iobuf = alloc_iob ( ring->mtu );
 | 
		
	
		
			
			|  | 161 | +		if ( ! iobuf ) {
 | 
		
	
		
			
			|  | 162 | +			/* Wait for next refill */
 | 
		
	
		
			
			|  | 163 | +			break;
 | 
		
	
		
			
			|  | 164 | +		}
 | 
		
	
		
			
			|  | 165 | +		iob_put ( iobuf, ring->mtu );
 | 
		
	
		
			
			|  | 166 | +
 | 
		
	
		
			
			|  | 167 | +		/* Enqueue I/O buffer */
 | 
		
	
		
			
			|  | 168 | +		if ( ( rc = usb_stream ( &ring->ep, iobuf, 0 ) ) != 0 ) {
 | 
		
	
		
			
			|  | 169 | +			netdev_rx_err ( netdev, iob_disown ( iobuf ), rc );
 | 
		
	
		
			
			|  | 170 | +			break;
 | 
		
	
		
			
			|  | 171 | +		}
 | 
		
	
		
			
			|  | 172 | +
 | 
		
	
		
			
			|  | 173 | +		/* Increment fill level */
 | 
		
	
		
			
			|  | 174 | +		ring->fill++;
 | 
		
	
		
			
			|  | 175 | +		profile_stop ( &ecm_refill_profiler );
 | 
		
	
		
			
			|  | 176 | +	}
 | 
		
	
		
			
			|  | 177 | +}
 | 
		
	
		
			
			|  | 178 | +
 | 
		
	
		
			
			|  | 179 | +/******************************************************************************
 | 
		
	
		
			
			|  | 180 | + *
 | 
		
	
		
			
			|  | 181 | + * CDC-ECM communications interface
 | 
		
	
		
			
			|  | 182 | + *
 | 
		
	
		
			
			|  | 183 | + ******************************************************************************
 | 
		
	
		
			
			|  | 184 | + */
 | 
		
	
		
			
			|  | 185 | +
 | 
		
	
		
			
			|  | 186 | +/**
 | 
		
	
		
			
			|  | 187 | + * Complete interrupt transfer
 | 
		
	
		
			
			|  | 188 | + *
 | 
		
	
		
			
			|  | 189 | + * @v ep		USB endpoint
 | 
		
	
		
			
			|  | 190 | + * @v iobuf		I/O buffer
 | 
		
	
		
			
			|  | 191 | + * @v rc		Completion status code
 | 
		
	
		
			
			|  | 192 | + */
 | 
		
	
		
			
			|  | 193 | +static void ecm_intr_complete ( struct usb_endpoint *ep,
 | 
		
	
		
			
			|  | 194 | +				struct io_buffer *iobuf, int rc ) {
 | 
		
	
		
			
			|  | 195 | +	struct ecm_device *ecm = container_of ( ep, struct ecm_device, intr.ep);
 | 
		
	
		
			
			|  | 196 | +	struct net_device *netdev = ecm->netdev;
 | 
		
	
		
			
			|  | 197 | +	struct usb_setup_packet *message;
 | 
		
	
		
			
			|  | 198 | +	size_t len = iob_len ( iobuf );
 | 
		
	
		
			
			|  | 199 | +
 | 
		
	
		
			
			|  | 200 | +	/* Profile completions */
 | 
		
	
		
			
			|  | 201 | +	profile_start ( &ecm_intr_profiler );
 | 
		
	
		
			
			|  | 202 | +
 | 
		
	
		
			
			|  | 203 | +	/* Decrement fill level */
 | 
		
	
		
			
			|  | 204 | +	assert ( ecm->intr.fill > 0 );
 | 
		
	
		
			
			|  | 205 | +	ecm->intr.fill--;
 | 
		
	
		
			
			|  | 206 | +
 | 
		
	
		
			
			|  | 207 | +	/* Ignore packets cancelled when the endpoint closes */
 | 
		
	
		
			
			|  | 208 | +	if ( ! ep->open )
 | 
		
	
		
			
			|  | 209 | +		goto ignore;
 | 
		
	
		
			
			|  | 210 | +
 | 
		
	
		
			
			|  | 211 | +	/* Drop packets with errors */
 | 
		
	
		
			
			|  | 212 | +	if ( rc != 0 ) {
 | 
		
	
		
			
			|  | 213 | +		DBGC ( ecm, "ECM %p interrupt failed: %s\n",
 | 
		
	
		
			
			|  | 214 | +		       ecm, strerror ( rc ) );
 | 
		
	
		
			
			|  | 215 | +		DBGC_HDA ( ecm, 0, iobuf->data, iob_len ( iobuf ) );
 | 
		
	
		
			
			|  | 216 | +		goto error;
 | 
		
	
		
			
			|  | 217 | +	}
 | 
		
	
		
			
			|  | 218 | +
 | 
		
	
		
			
			|  | 219 | +	/* Extract message header */
 | 
		
	
		
			
			|  | 220 | +	if ( len < sizeof ( *message ) ) {
 | 
		
	
		
			
			|  | 221 | +		DBGC ( ecm, "ECM %p underlength interrupt:\n", ecm );
 | 
		
	
		
			
			|  | 222 | +		DBGC_HDA ( ecm, 0, iobuf->data, iob_len ( iobuf ) );
 | 
		
	
		
			
			|  | 223 | +		goto error;
 | 
		
	
		
			
			|  | 224 | +	}
 | 
		
	
		
			
			|  | 225 | +	message = iobuf->data;
 | 
		
	
		
			
			|  | 226 | +
 | 
		
	
		
			
			|  | 227 | +	/* Parse message header */
 | 
		
	
		
			
			|  | 228 | +	switch ( message->request ) {
 | 
		
	
		
			
			|  | 229 | +
 | 
		
	
		
			
			|  | 230 | +	case cpu_to_le16 ( CDC_NETWORK_CONNECTION ) :
 | 
		
	
		
			
			|  | 231 | +		if ( message->value && ! netdev_link_ok ( netdev ) ) {
 | 
		
	
		
			
			|  | 232 | +			DBGC ( ecm, "ECM %p link up\n", ecm );
 | 
		
	
		
			
			|  | 233 | +			netdev_link_up ( netdev );
 | 
		
	
		
			
			|  | 234 | +		} else if ( netdev_link_ok ( netdev ) && ! message->value ) {
 | 
		
	
		
			
			|  | 235 | +			DBGC ( ecm, "ECM %p link down\n", ecm );
 | 
		
	
		
			
			|  | 236 | +			netdev_link_down ( netdev );
 | 
		
	
		
			
			|  | 237 | +		}
 | 
		
	
		
			
			|  | 238 | +		break;
 | 
		
	
		
			
			|  | 239 | +
 | 
		
	
		
			
			|  | 240 | +	case cpu_to_le16 ( CDC_CONNECTION_SPEED_CHANGE ) :
 | 
		
	
		
			
			|  | 241 | +		/* Ignore */
 | 
		
	
		
			
			|  | 242 | +		break;
 | 
		
	
		
			
			|  | 243 | +
 | 
		
	
		
			
			|  | 244 | +	default:
 | 
		
	
		
			
			|  | 245 | +		DBGC ( ecm, "ECM %p unrecognised interrupt:\n", ecm );
 | 
		
	
		
			
			|  | 246 | +		DBGC_HDA ( ecm, 0, iobuf->data, iob_len ( iobuf ) );
 | 
		
	
		
			
			|  | 247 | +		goto error;
 | 
		
	
		
			
			|  | 248 | +	}
 | 
		
	
		
			
			|  | 249 | +
 | 
		
	
		
			
			|  | 250 | +	/* Free I/O buffer */
 | 
		
	
		
			
			|  | 251 | +	free_iob ( iobuf );
 | 
		
	
		
			
			|  | 252 | +	profile_stop ( &ecm_intr_profiler );
 | 
		
	
		
			
			|  | 253 | +
 | 
		
	
		
			
			|  | 254 | +	return;
 | 
		
	
		
			
			|  | 255 | +
 | 
		
	
		
			
			|  | 256 | + error:
 | 
		
	
		
			
			|  | 257 | +	netdev_rx_err ( netdev, iob_disown ( iobuf ), rc );
 | 
		
	
		
			
			|  | 258 | + ignore:
 | 
		
	
		
			
			|  | 259 | +	free_iob ( iobuf );
 | 
		
	
		
			
			|  | 260 | +	return;
 | 
		
	
		
			
			|  | 261 | +}
 | 
		
	
		
			
			|  | 262 | +
 | 
		
	
		
			
			|  | 263 | +/** Interrupt endpoint operations */
 | 
		
	
		
			
			|  | 264 | +static struct usb_endpoint_driver_operations ecm_intr_operations = {
 | 
		
	
		
			
			|  | 265 | +	.complete = ecm_intr_complete,
 | 
		
	
		
			
			|  | 266 | +};
 | 
		
	
		
			
			|  | 267 | +
 | 
		
	
		
			
			|  | 268 | +/**
 | 
		
	
		
			
			|  | 269 | + * Open communications interface
 | 
		
	
		
			
			|  | 270 | + *
 | 
		
	
		
			
			|  | 271 | + * @v ecm		CDC-ECM device
 | 
		
	
		
			
			|  | 272 | + * @ret rc		Return status code
 | 
		
	
		
			
			|  | 273 | + */
 | 
		
	
		
			
			|  | 274 | +static int ecm_comms_open ( struct ecm_device *ecm ) {
 | 
		
	
		
			
			|  | 275 | +	int rc;
 | 
		
	
		
			
			|  | 276 | +
 | 
		
	
		
			
			|  | 277 | +	/* Open interrupt endpoint */
 | 
		
	
		
			
			|  | 278 | +	if ( ( rc = usb_endpoint_open ( &ecm->intr.ep ) ) != 0 ) {
 | 
		
	
		
			
			|  | 279 | +		DBGC ( ecm, "ECM %p could not open interrupt: %s\n",
 | 
		
	
		
			
			|  | 280 | +		       ecm, strerror ( rc ) );
 | 
		
	
		
			
			|  | 281 | +		goto err_open;
 | 
		
	
		
			
			|  | 282 | +	}
 | 
		
	
		
			
			|  | 283 | +
 | 
		
	
		
			
			|  | 284 | +	/* Refill interrupt ring */
 | 
		
	
		
			
			|  | 285 | +	ecm_rx_refill ( ecm, &ecm->intr );
 | 
		
	
		
			
			|  | 286 | +
 | 
		
	
		
			
			|  | 287 | +	return 0;
 | 
		
	
		
			
			|  | 288 | +
 | 
		
	
		
			
			|  | 289 | +	usb_endpoint_close ( &ecm->intr.ep );
 | 
		
	
		
			
			|  | 290 | +	assert ( ecm->intr.fill == 0 );
 | 
		
	
		
			
			|  | 291 | + err_open:
 | 
		
	
		
			
			|  | 292 | +	return rc;
 | 
		
	
		
			
			|  | 293 | +}
 | 
		
	
		
			
			|  | 294 | +
 | 
		
	
		
			
			|  | 295 | +/**
 | 
		
	
		
			
			|  | 296 | + * Close communications interface
 | 
		
	
		
			
			|  | 297 | + *
 | 
		
	
		
			
			|  | 298 | + * @v ecm		CDC-ECM device
 | 
		
	
		
			
			|  | 299 | + */
 | 
		
	
		
			
			|  | 300 | +static void ecm_comms_close ( struct ecm_device *ecm ) {
 | 
		
	
		
			
			|  | 301 | +
 | 
		
	
		
			
			|  | 302 | +	/* Close interrupt endpoint */
 | 
		
	
		
			
			|  | 303 | +	usb_endpoint_close ( &ecm->intr.ep );
 | 
		
	
		
			
			|  | 304 | +	assert ( ecm->intr.fill == 0 );
 | 
		
	
		
			
			|  | 305 | +}
 | 
		
	
		
			
			|  | 306 | +
 | 
		
	
		
			
			|  | 307 | +/******************************************************************************
 | 
		
	
		
			
			|  | 308 | + *
 | 
		
	
		
			
			|  | 309 | + * CDC-ECM data interface
 | 
		
	
		
			
			|  | 310 | + *
 | 
		
	
		
			
			|  | 311 | + ******************************************************************************
 | 
		
	
		
			
			|  | 312 | + */
 | 
		
	
		
			
			|  | 313 | +
 | 
		
	
		
			
			|  | 314 | +/**
 | 
		
	
		
			
			|  | 315 | + * Complete bulk IN transfer
 | 
		
	
		
			
			|  | 316 | + *
 | 
		
	
		
			
			|  | 317 | + * @v ep		USB endpoint
 | 
		
	
		
			
			|  | 318 | + * @v iobuf		I/O buffer
 | 
		
	
		
			
			|  | 319 | + * @v rc		Completion status code
 | 
		
	
		
			
			|  | 320 | + */
 | 
		
	
		
			
			|  | 321 | +static void ecm_in_complete ( struct usb_endpoint *ep, struct io_buffer *iobuf,
 | 
		
	
		
			
			|  | 322 | +			      int rc ) {
 | 
		
	
		
			
			|  | 323 | +	struct ecm_device *ecm = container_of ( ep, struct ecm_device, in.ep );
 | 
		
	
		
			
			|  | 324 | +	struct net_device *netdev = ecm->netdev;
 | 
		
	
		
			
			|  | 325 | +
 | 
		
	
		
			
			|  | 326 | +	/* Profile receive completions */
 | 
		
	
		
			
			|  | 327 | +	profile_start ( &ecm_in_profiler );
 | 
		
	
		
			
			|  | 328 | +
 | 
		
	
		
			
			|  | 329 | +	/* Decrement fill level */
 | 
		
	
		
			
			|  | 330 | +	assert ( ecm->in.fill > 0 );
 | 
		
	
		
			
			|  | 331 | +	ecm->in.fill--;
 | 
		
	
		
			
			|  | 332 | +
 | 
		
	
		
			
			|  | 333 | +	/* Ignore packets cancelled when the endpoint closes */
 | 
		
	
		
			
			|  | 334 | +	if ( ! ep->open )
 | 
		
	
		
			
			|  | 335 | +		goto ignore;
 | 
		
	
		
			
			|  | 336 | +
 | 
		
	
		
			
			|  | 337 | +	/* Record USB errors against the network device */
 | 
		
	
		
			
			|  | 338 | +	if ( rc != 0 ) {
 | 
		
	
		
			
			|  | 339 | +		DBGC ( ecm, "ECM %p bulk IN failed: %s\n",
 | 
		
	
		
			
			|  | 340 | +		       ecm, strerror ( rc ) );
 | 
		
	
		
			
			|  | 341 | +		goto error;
 | 
		
	
		
			
			|  | 342 | +	}
 | 
		
	
		
			
			|  | 343 | +
 | 
		
	
		
			
			|  | 344 | +	/* Hand off to network stack */
 | 
		
	
		
			
			|  | 345 | +	netdev_rx ( netdev, iob_disown ( iobuf ) );
 | 
		
	
		
			
			|  | 346 | +
 | 
		
	
		
			
			|  | 347 | +	profile_stop ( &ecm_in_profiler );
 | 
		
	
		
			
			|  | 348 | +	return;
 | 
		
	
		
			
			|  | 349 | +
 | 
		
	
		
			
			|  | 350 | + error:
 | 
		
	
		
			
			|  | 351 | +	netdev_rx_err ( netdev, iob_disown ( iobuf ), rc );
 | 
		
	
		
			
			|  | 352 | + ignore:
 | 
		
	
		
			
			|  | 353 | +	free_iob ( iobuf );
 | 
		
	
		
			
			|  | 354 | +}
 | 
		
	
		
			
			|  | 355 | +
 | 
		
	
		
			
			|  | 356 | +/** Bulk IN endpoint operations */
 | 
		
	
		
			
			|  | 357 | +static struct usb_endpoint_driver_operations ecm_in_operations = {
 | 
		
	
		
			
			|  | 358 | +	.complete = ecm_in_complete,
 | 
		
	
		
			
			|  | 359 | +};
 | 
		
	
		
			
			|  | 360 | +
 | 
		
	
		
			
			|  | 361 | +/**
 | 
		
	
		
			
			|  | 362 | + * Transmit packet
 | 
		
	
		
			
			|  | 363 | + *
 | 
		
	
		
			
			|  | 364 | + * @v ecm		CDC-ECM device
 | 
		
	
		
			
			|  | 365 | + * @v iobuf		I/O buffer
 | 
		
	
		
			
			|  | 366 | + * @ret rc		Return status code
 | 
		
	
		
			
			|  | 367 | + */
 | 
		
	
		
			
			|  | 368 | +static int ecm_out_transmit ( struct ecm_device *ecm,
 | 
		
	
		
			
			|  | 369 | +			      struct io_buffer *iobuf ) {
 | 
		
	
		
			
			|  | 370 | +	int rc;
 | 
		
	
		
			
			|  | 371 | +
 | 
		
	
		
			
			|  | 372 | +	/* Profile transmissions */
 | 
		
	
		
			
			|  | 373 | +	profile_start ( &ecm_out_profiler );
 | 
		
	
		
			
			|  | 374 | +
 | 
		
	
		
			
			|  | 375 | +	/* Enqueue I/O buffer */
 | 
		
	
		
			
			|  | 376 | +	if ( ( rc = usb_stream ( &ecm->out.ep, iobuf, 1 ) ) != 0 )
 | 
		
	
		
			
			|  | 377 | +		return rc;
 | 
		
	
		
			
			|  | 378 | +
 | 
		
	
		
			
			|  | 379 | +	profile_stop ( &ecm_out_profiler );
 | 
		
	
		
			
			|  | 380 | +	return 0;
 | 
		
	
		
			
			|  | 381 | +}
 | 
		
	
		
			
			|  | 382 | +
 | 
		
	
		
			
			|  | 383 | +/**
 | 
		
	
		
			
			|  | 384 | + * Complete bulk OUT transfer
 | 
		
	
		
			
			|  | 385 | + *
 | 
		
	
		
			
			|  | 386 | + * @v ep		USB endpoint
 | 
		
	
		
			
			|  | 387 | + * @v iobuf		I/O buffer
 | 
		
	
		
			
			|  | 388 | + * @v rc		Completion status code
 | 
		
	
		
			
			|  | 389 | + */
 | 
		
	
		
			
			|  | 390 | +static void ecm_out_complete ( struct usb_endpoint *ep, struct io_buffer *iobuf,
 | 
		
	
		
			
			|  | 391 | +			       int rc ) {
 | 
		
	
		
			
			|  | 392 | +	struct ecm_device *ecm = container_of ( ep, struct ecm_device, out.ep );
 | 
		
	
		
			
			|  | 393 | +	struct net_device *netdev = ecm->netdev;
 | 
		
	
		
			
			|  | 394 | +
 | 
		
	
		
			
			|  | 395 | +	/* Report TX completion */
 | 
		
	
		
			
			|  | 396 | +	netdev_tx_complete_err ( netdev, iobuf, rc );
 | 
		
	
		
			
			|  | 397 | +}
 | 
		
	
		
			
			|  | 398 | +
 | 
		
	
		
			
			|  | 399 | +/** Bulk OUT endpoint operations */
 | 
		
	
		
			
			|  | 400 | +static struct usb_endpoint_driver_operations ecm_out_operations = {
 | 
		
	
		
			
			|  | 401 | +	.complete = ecm_out_complete,
 | 
		
	
		
			
			|  | 402 | +};
 | 
		
	
		
			
			|  | 403 | +
 | 
		
	
		
			
			|  | 404 | +/**
 | 
		
	
		
			
			|  | 405 | + * Open data interface
 | 
		
	
		
			
			|  | 406 | + *
 | 
		
	
		
			
			|  | 407 | + * @v ecm		CDC-ECM device
 | 
		
	
		
			
			|  | 408 | + * @ret rc		Return status code
 | 
		
	
		
			
			|  | 409 | + */
 | 
		
	
		
			
			|  | 410 | +static int ecm_data_open ( struct ecm_device *ecm ) {
 | 
		
	
		
			
			|  | 411 | +	struct usb_device *usb = ecm->usb;
 | 
		
	
		
			
			|  | 412 | +	int rc;
 | 
		
	
		
			
			|  | 413 | +
 | 
		
	
		
			
			|  | 414 | +	/* Select alternate setting for data interface */
 | 
		
	
		
			
			|  | 415 | +	if ( ( rc = usb_set_interface ( usb, ecm->data,
 | 
		
	
		
			
			|  | 416 | +					ECM_DATA_ALTERNATE ) ) != 0 ) {
 | 
		
	
		
			
			|  | 417 | +		DBGC ( ecm, "ECM %p could not set alternate interface: %s\n",
 | 
		
	
		
			
			|  | 418 | +		       ecm, strerror ( rc ) );
 | 
		
	
		
			
			|  | 419 | +		goto err_set_interface;
 | 
		
	
		
			
			|  | 420 | +	}
 | 
		
	
		
			
			|  | 421 | +
 | 
		
	
		
			
			|  | 422 | +	/* Open bulk IN endpoint */
 | 
		
	
		
			
			|  | 423 | +	if ( ( rc = usb_endpoint_open ( &ecm->in.ep ) ) != 0 ) {
 | 
		
	
		
			
			|  | 424 | +		DBGC ( ecm, "ECM %p could not open bulk IN: %s\n",
 | 
		
	
		
			
			|  | 425 | +		       ecm, strerror ( rc ) );
 | 
		
	
		
			
			|  | 426 | +		goto err_open_in;
 | 
		
	
		
			
			|  | 427 | +	}
 | 
		
	
		
			
			|  | 428 | +
 | 
		
	
		
			
			|  | 429 | +	/* Open bulk OUT endpoint */
 | 
		
	
		
			
			|  | 430 | +	if ( ( rc = usb_endpoint_open ( &ecm->out.ep ) ) != 0 ) {
 | 
		
	
		
			
			|  | 431 | +		DBGC ( ecm, "ECM %p could not open bulk OUT: %s\n",
 | 
		
	
		
			
			|  | 432 | +		       ecm, strerror ( rc ) );
 | 
		
	
		
			
			|  | 433 | +		goto err_open_out;
 | 
		
	
		
			
			|  | 434 | +	}
 | 
		
	
		
			
			|  | 435 | +
 | 
		
	
		
			
			|  | 436 | +	/* Refill bulk IN ring */
 | 
		
	
		
			
			|  | 437 | +	ecm_rx_refill ( ecm, &ecm->in );
 | 
		
	
		
			
			|  | 438 | +
 | 
		
	
		
			
			|  | 439 | +	return 0;
 | 
		
	
		
			
			|  | 440 | +
 | 
		
	
		
			
			|  | 441 | +	usb_endpoint_close ( &ecm->out.ep );
 | 
		
	
		
			
			|  | 442 | + err_open_out:
 | 
		
	
		
			
			|  | 443 | +	usb_endpoint_close ( &ecm->in.ep );
 | 
		
	
		
			
			|  | 444 | +	assert ( ecm->in.fill == 0 );
 | 
		
	
		
			
			|  | 445 | + err_open_in:
 | 
		
	
		
			
			|  | 446 | +	usb_set_interface ( usb, ecm->data, 0 );
 | 
		
	
		
			
			|  | 447 | + err_set_interface:
 | 
		
	
		
			
			|  | 448 | +	return rc;
 | 
		
	
		
			
			|  | 449 | +}
 | 
		
	
		
			
			|  | 450 | +
 | 
		
	
		
			
			|  | 451 | +/**
 | 
		
	
		
			
			|  | 452 | + * Close data interface
 | 
		
	
		
			
			|  | 453 | + *
 | 
		
	
		
			
			|  | 454 | + * @v ecm		CDC-ECM device
 | 
		
	
		
			
			|  | 455 | + */
 | 
		
	
		
			
			|  | 456 | +static void ecm_data_close ( struct ecm_device *ecm ) {
 | 
		
	
		
			
			|  | 457 | +	struct usb_device *usb = ecm->usb;
 | 
		
	
		
			
			|  | 458 | +
 | 
		
	
		
			
			|  | 459 | +	/* Close endpoints */
 | 
		
	
		
			
			|  | 460 | +	usb_endpoint_close ( &ecm->out.ep );
 | 
		
	
		
			
			|  | 461 | +	usb_endpoint_close ( &ecm->in.ep );
 | 
		
	
		
			
			|  | 462 | +	assert ( ecm->in.fill == 0 );
 | 
		
	
		
			
			|  | 463 | +
 | 
		
	
		
			
			|  | 464 | +	/* Reset data interface */
 | 
		
	
		
			
			|  | 465 | +	usb_set_interface ( usb, ecm->data, 0 );
 | 
		
	
		
			
			|  | 466 | +}
 | 
		
	
		
			
			|  | 467 | +
 | 
		
	
		
			
			|  | 468 | +/******************************************************************************
 | 
		
	
		
			
			|  | 469 | + *
 | 
		
	
		
			
			|  | 470 | + * Network device interface
 | 
		
	
		
			
			|  | 471 | + *
 | 
		
	
		
			
			|  | 472 | + ******************************************************************************
 | 
		
	
		
			
			|  | 473 | + */
 | 
		
	
		
			
			|  | 474 | +
 | 
		
	
		
			
			|  | 475 | +/**
 | 
		
	
		
			
			|  | 476 | + * Open network device
 | 
		
	
		
			
			|  | 477 | + *
 | 
		
	
		
			
			|  | 478 | + * @v netdev		Network device
 | 
		
	
		
			
			|  | 479 | + * @ret rc		Return status code
 | 
		
	
		
			
			|  | 480 | + */
 | 
		
	
		
			
			|  | 481 | +static int ecm_open ( struct net_device *netdev ) {
 | 
		
	
		
			
			|  | 482 | +	struct ecm_device *ecm = netdev->priv;
 | 
		
	
		
			
			|  | 483 | +	struct usb_device *usb = ecm->usb;
 | 
		
	
		
			
			|  | 484 | +	unsigned int filter;
 | 
		
	
		
			
			|  | 485 | +	int rc;
 | 
		
	
		
			
			|  | 486 | +
 | 
		
	
		
			
			|  | 487 | +	/* Open communications interface */
 | 
		
	
		
			
			|  | 488 | +	if ( ( rc = ecm_comms_open ( ecm ) ) != 0 )
 | 
		
	
		
			
			|  | 489 | +		goto err_comms_open;
 | 
		
	
		
			
			|  | 490 | +
 | 
		
	
		
			
			|  | 491 | +	/* Open data interface */
 | 
		
	
		
			
			|  | 492 | +	if ( ( rc = ecm_data_open ( ecm ) ) != 0 )
 | 
		
	
		
			
			|  | 493 | +		goto err_data_open;
 | 
		
	
		
			
			|  | 494 | +
 | 
		
	
		
			
			|  | 495 | +	/* Set packet filter */
 | 
		
	
		
			
			|  | 496 | +	filter = ( ECM_PACKET_TYPE_PROMISCUOUS |
 | 
		
	
		
			
			|  | 497 | +		   ECM_PACKET_TYPE_ALL_MULTICAST |
 | 
		
	
		
			
			|  | 498 | +		   ECM_PACKET_TYPE_DIRECTED |
 | 
		
	
		
			
			|  | 499 | +		   ECM_PACKET_TYPE_BROADCAST );
 | 
		
	
		
			
			|  | 500 | +	if ( ( rc = usb_control ( usb, ECM_SET_ETHERNET_PACKET_FILTER,
 | 
		
	
		
			
			|  | 501 | +				  filter, ecm->comms, NULL, 0 ) ) != 0 ) {
 | 
		
	
		
			
			|  | 502 | +		DBGC ( ecm, "ECM %p could not set packet filter: %s\n",
 | 
		
	
		
			
			|  | 503 | +		       ecm, strerror ( rc ) );
 | 
		
	
		
			
			|  | 504 | +		goto err_set_filter;
 | 
		
	
		
			
			|  | 505 | +	}
 | 
		
	
		
			
			|  | 506 | +
 | 
		
	
		
			
			|  | 507 | +	return 0;
 | 
		
	
		
			
			|  | 508 | +
 | 
		
	
		
			
			|  | 509 | + err_set_filter:
 | 
		
	
		
			
			|  | 510 | +	ecm_data_close ( ecm );
 | 
		
	
		
			
			|  | 511 | + err_data_open:
 | 
		
	
		
			
			|  | 512 | +	ecm_comms_close ( ecm );
 | 
		
	
		
			
			|  | 513 | + err_comms_open:
 | 
		
	
		
			
			|  | 514 | +	return rc;
 | 
		
	
		
			
			|  | 515 | +}
 | 
		
	
		
			
			|  | 516 | +
 | 
		
	
		
			
			|  | 517 | +/**
 | 
		
	
		
			
			|  | 518 | + * Close network device
 | 
		
	
		
			
			|  | 519 | + *
 | 
		
	
		
			
			|  | 520 | + * @v netdev		Network device
 | 
		
	
		
			
			|  | 521 | + */
 | 
		
	
		
			
			|  | 522 | +static void ecm_close ( struct net_device *netdev ) {
 | 
		
	
		
			
			|  | 523 | +	struct ecm_device *ecm = netdev->priv;
 | 
		
	
		
			
			|  | 524 | +
 | 
		
	
		
			
			|  | 525 | +	/* Close data interface */
 | 
		
	
		
			
			|  | 526 | +	ecm_data_close ( ecm );
 | 
		
	
		
			
			|  | 527 | +
 | 
		
	
		
			
			|  | 528 | +	/* Close communications interface */
 | 
		
	
		
			
			|  | 529 | +	ecm_comms_close ( ecm );
 | 
		
	
		
			
			|  | 530 | +}
 | 
		
	
		
			
			|  | 531 | +
 | 
		
	
		
			
			|  | 532 | +/**
 | 
		
	
		
			
			|  | 533 | + * Transmit packet
 | 
		
	
		
			
			|  | 534 | + *
 | 
		
	
		
			
			|  | 535 | + * @v netdev		Network device
 | 
		
	
		
			
			|  | 536 | + * @v iobuf		I/O buffer
 | 
		
	
		
			
			|  | 537 | + * @ret rc		Return status code
 | 
		
	
		
			
			|  | 538 | + */
 | 
		
	
		
			
			|  | 539 | +static int ecm_transmit ( struct net_device *netdev,
 | 
		
	
		
			
			|  | 540 | +			  struct io_buffer *iobuf ) {
 | 
		
	
		
			
			|  | 541 | +	struct ecm_device *ecm = netdev->priv;
 | 
		
	
		
			
			|  | 542 | +	int rc;
 | 
		
	
		
			
			|  | 543 | +
 | 
		
	
		
			
			|  | 544 | +	/* Transmit packet */
 | 
		
	
		
			
			|  | 545 | +	if ( ( rc = ecm_out_transmit ( ecm, iobuf ) ) != 0 )
 | 
		
	
		
			
			|  | 546 | +		return rc;
 | 
		
	
		
			
			|  | 547 | +
 | 
		
	
		
			
			|  | 548 | +	return 0;
 | 
		
	
		
			
			|  | 549 | +}
 | 
		
	
		
			
			|  | 550 | +
 | 
		
	
		
			
			|  | 551 | +/**
 | 
		
	
		
			
			|  | 552 | + * Poll for completed and received packets
 | 
		
	
		
			
			|  | 553 | + *
 | 
		
	
		
			
			|  | 554 | + * @v netdev		Network device
 | 
		
	
		
			
			|  | 555 | + */
 | 
		
	
		
			
			|  | 556 | +static void ecm_poll ( struct net_device *netdev ) {
 | 
		
	
		
			
			|  | 557 | +	struct ecm_device *ecm = netdev->priv;
 | 
		
	
		
			
			|  | 558 | +
 | 
		
	
		
			
			|  | 559 | +	/* Poll USB bus */
 | 
		
	
		
			
			|  | 560 | +	usb_poll ( ecm->bus );
 | 
		
	
		
			
			|  | 561 | +
 | 
		
	
		
			
			|  | 562 | +	/* Refill interrupt ring */
 | 
		
	
		
			
			|  | 563 | +	ecm_rx_refill ( ecm, &ecm->intr );
 | 
		
	
		
			
			|  | 564 | +
 | 
		
	
		
			
			|  | 565 | +	/* Refill bulk IN ring */
 | 
		
	
		
			
			|  | 566 | +	ecm_rx_refill ( ecm, &ecm->in );
 | 
		
	
		
			
			|  | 567 | +}
 | 
		
	
		
			
			|  | 568 | +
 | 
		
	
		
			
			|  | 569 | +/** CDC-ECM network device operations */
 | 
		
	
		
			
			|  | 570 | +static struct net_device_operations ecm_operations = {
 | 
		
	
		
			
			|  | 571 | +	.open		= ecm_open,
 | 
		
	
		
			
			|  | 572 | +	.close		= ecm_close,
 | 
		
	
		
			
			|  | 573 | +	.transmit	= ecm_transmit,
 | 
		
	
		
			
			|  | 574 | +	.poll		= ecm_poll,
 | 
		
	
		
			
			|  | 575 | +};
 | 
		
	
		
			
			|  | 576 | +
 | 
		
	
		
			
			|  | 577 | +/******************************************************************************
 | 
		
	
		
			
			|  | 578 | + *
 | 
		
	
		
			
			|  | 579 | + * USB interface
 | 
		
	
		
			
			|  | 580 | + *
 | 
		
	
		
			
			|  | 581 | + ******************************************************************************
 | 
		
	
		
			
			|  | 582 | + */
 | 
		
	
		
			
			|  | 583 | +
 | 
		
	
		
			
			|  | 584 | +/**
 | 
		
	
		
			
			|  | 585 | + * Probe device
 | 
		
	
		
			
			|  | 586 | + *
 | 
		
	
		
			
			|  | 587 | + * @v func		USB function
 | 
		
	
		
			
			|  | 588 | + * @v config		Configuration descriptor
 | 
		
	
		
			
			|  | 589 | + * @ret rc		Return status code
 | 
		
	
		
			
			|  | 590 | + */
 | 
		
	
		
			
			|  | 591 | +static int ecm_probe ( struct usb_function *func,
 | 
		
	
		
			
			|  | 592 | +		       struct usb_configuration_descriptor *config ) {
 | 
		
	
		
			
			|  | 593 | +	struct usb_device *usb = func->usb;
 | 
		
	
		
			
			|  | 594 | +	struct net_device *netdev;
 | 
		
	
		
			
			|  | 595 | +	struct ecm_device *ecm;
 | 
		
	
		
			
			|  | 596 | +	struct usb_interface_descriptor *comms;
 | 
		
	
		
			
			|  | 597 | +	struct usb_interface_descriptor *data;
 | 
		
	
		
			
			|  | 598 | +	struct ecm_ethernet_descriptor *ethernet;
 | 
		
	
		
			
			|  | 599 | +	int rc;
 | 
		
	
		
			
			|  | 600 | +
 | 
		
	
		
			
			|  | 601 | +	/* Allocate and initialise structure */
 | 
		
	
		
			
			|  | 602 | +	netdev = alloc_etherdev ( sizeof ( *ecm ) );
 | 
		
	
		
			
			|  | 603 | +	if ( ! netdev ) {
 | 
		
	
		
			
			|  | 604 | +		rc = -ENOMEM;
 | 
		
	
		
			
			|  | 605 | +		goto err_alloc;
 | 
		
	
		
			
			|  | 606 | +	}
 | 
		
	
		
			
			|  | 607 | +	netdev_init ( netdev, &ecm_operations );
 | 
		
	
		
			
			|  | 608 | +	netdev->dev = &func->dev;
 | 
		
	
		
			
			|  | 609 | +	ecm = netdev->priv;
 | 
		
	
		
			
			|  | 610 | +	memset ( ecm, 0, sizeof ( *ecm ) );
 | 
		
	
		
			
			|  | 611 | +	ecm->usb = usb;
 | 
		
	
		
			
			|  | 612 | +	ecm->bus = usb->port->hub->bus;
 | 
		
	
		
			
			|  | 613 | +	ecm->netdev = netdev;
 | 
		
	
		
			
			|  | 614 | +	usb_endpoint_init ( &ecm->intr.ep, usb, &ecm_intr_operations );
 | 
		
	
		
			
			|  | 615 | +	usb_endpoint_init ( &ecm->in.ep, usb, &ecm_in_operations );
 | 
		
	
		
			
			|  | 616 | +	usb_endpoint_init ( &ecm->out.ep, usb, &ecm_out_operations );
 | 
		
	
		
			
			|  | 617 | +	DBGC ( ecm, "ECM %p on %s\n", ecm, func->name );
 | 
		
	
		
			
			|  | 618 | +
 | 
		
	
		
			
			|  | 619 | +	/* Identify interfaces */
 | 
		
	
		
			
			|  | 620 | +	if ( func->count < ECM_INTERFACE_COUNT ) {
 | 
		
	
		
			
			|  | 621 | +		DBGC ( ecm, "ECM %p has only %d interfaces\n",
 | 
		
	
		
			
			|  | 622 | +		       ecm, func->count );
 | 
		
	
		
			
			|  | 623 | +		rc = -EINVAL;
 | 
		
	
		
			
			|  | 624 | +		goto err_count;
 | 
		
	
		
			
			|  | 625 | +	}
 | 
		
	
		
			
			|  | 626 | +	ecm->comms = func->interface[ECM_INTERFACE_COMMS];
 | 
		
	
		
			
			|  | 627 | +	ecm->data = func->interface[ECM_INTERFACE_DATA];
 | 
		
	
		
			
			|  | 628 | +
 | 
		
	
		
			
			|  | 629 | +	/* Locate communications interface descriptor */
 | 
		
	
		
			
			|  | 630 | +	comms = usb_interface_descriptor ( config, ecm->comms, 0 );
 | 
		
	
		
			
			|  | 631 | +	if ( ! comms ) {
 | 
		
	
		
			
			|  | 632 | +		DBGC ( ecm, "ECM %p has no communications interface\n", ecm );
 | 
		
	
		
			
			|  | 633 | +		rc = -EINVAL;
 | 
		
	
		
			
			|  | 634 | +		goto err_comms;
 | 
		
	
		
			
			|  | 635 | +	}
 | 
		
	
		
			
			|  | 636 | +
 | 
		
	
		
			
			|  | 637 | +	/* Locate data interface descriptor */
 | 
		
	
		
			
			|  | 638 | +	data = usb_interface_descriptor ( config, ecm->data,
 | 
		
	
		
			
			|  | 639 | +					  ECM_DATA_ALTERNATE );
 | 
		
	
		
			
			|  | 640 | +	if ( ! data ) {
 | 
		
	
		
			
			|  | 641 | +		DBGC ( ecm, "ECM %p has no data interface\n", ecm );
 | 
		
	
		
			
			|  | 642 | +		rc = -EINVAL;
 | 
		
	
		
			
			|  | 643 | +		goto err_data;
 | 
		
	
		
			
			|  | 644 | +	}
 | 
		
	
		
			
			|  | 645 | +
 | 
		
	
		
			
			|  | 646 | +	/* Describe interrupt endpoint */
 | 
		
	
		
			
			|  | 647 | +	if ( ( rc = usb_endpoint_described ( &ecm->intr.ep, config, comms,
 | 
		
	
		
			
			|  | 648 | +					     USB_INTERRUPT, 0 ) ) != 0 ) {
 | 
		
	
		
			
			|  | 649 | +		DBGC ( ecm, "ECM %p could not describe interrupt endpoint: "
 | 
		
	
		
			
			|  | 650 | +		       "%s\n", ecm, strerror ( rc ) );
 | 
		
	
		
			
			|  | 651 | +		goto err_interrupt;
 | 
		
	
		
			
			|  | 652 | +	}
 | 
		
	
		
			
			|  | 653 | +	ecm->intr.mtu = ecm->intr.ep.mtu;
 | 
		
	
		
			
			|  | 654 | +	ecm->intr.max = ECM_INTR_MAX_FILL;
 | 
		
	
		
			
			|  | 655 | +
 | 
		
	
		
			
			|  | 656 | +	/* Describe bulk IN endpoint */
 | 
		
	
		
			
			|  | 657 | +	if ( ( rc = usb_endpoint_described ( &ecm->in.ep, config, data,
 | 
		
	
		
			
			|  | 658 | +					     USB_BULK_IN, 0 ) ) != 0 ) {
 | 
		
	
		
			
			|  | 659 | +		DBGC ( ecm, "ECM %p could not describe bulk IN endpoint: "
 | 
		
	
		
			
			|  | 660 | +		       "%s\n", ecm, strerror ( rc ) );
 | 
		
	
		
			
			|  | 661 | +		goto err_bulk_in;
 | 
		
	
		
			
			|  | 662 | +	}
 | 
		
	
		
			
			|  | 663 | +	ecm->in.mtu = ECM_IN_MTU;
 | 
		
	
		
			
			|  | 664 | +	ecm->in.max = ECM_IN_MAX_FILL;
 | 
		
	
		
			
			|  | 665 | +
 | 
		
	
		
			
			|  | 666 | +	/* Describe bulk OUT endpoint */
 | 
		
	
		
			
			|  | 667 | +	if ( ( rc = usb_endpoint_described ( &ecm->out.ep, config, data,
 | 
		
	
		
			
			|  | 668 | +					     USB_BULK_OUT, 0 ) ) != 0 ) {
 | 
		
	
		
			
			|  | 669 | +		DBGC ( ecm, "ECM %p could not describe bulk OUT endpoint: "
 | 
		
	
		
			
			|  | 670 | +		       "%s\n", ecm, strerror ( rc ) );
 | 
		
	
		
			
			|  | 671 | +		goto err_bulk_out;
 | 
		
	
		
			
			|  | 672 | +	}
 | 
		
	
		
			
			|  | 673 | +
 | 
		
	
		
			
			|  | 674 | +	/* Locate Ethernet descriptor */
 | 
		
	
		
			
			|  | 675 | +	ethernet = ecm_ethernet_descriptor ( config, comms );
 | 
		
	
		
			
			|  | 676 | +	if ( ! ethernet ) {
 | 
		
	
		
			
			|  | 677 | +		DBGC ( ecm, "ECM %p has no Ethernet descriptor\n", ecm );
 | 
		
	
		
			
			|  | 678 | +		rc = -EINVAL;
 | 
		
	
		
			
			|  | 679 | +		goto err_ethernet;
 | 
		
	
		
			
			|  | 680 | +	}
 | 
		
	
		
			
			|  | 681 | +
 | 
		
	
		
			
			|  | 682 | +	/* Fetch MAC address */
 | 
		
	
		
			
			|  | 683 | +	if ( ( rc = ecm_fetch_mac ( usb, ethernet, netdev->hw_addr ) ) != 0 ) {
 | 
		
	
		
			
			|  | 684 | +		DBGC ( ecm, "ECM %p could not fetch MAC address: %s\n",
 | 
		
	
		
			
			|  | 685 | +		       ecm, strerror ( rc ) );
 | 
		
	
		
			
			|  | 686 | +		goto err_fetch_mac;
 | 
		
	
		
			
			|  | 687 | +	}
 | 
		
	
		
			
			|  | 688 | +
 | 
		
	
		
			
			|  | 689 | +	/* Register network device */
 | 
		
	
		
			
			|  | 690 | +	if ( ( rc = register_netdev ( netdev ) ) != 0 )
 | 
		
	
		
			
			|  | 691 | +		goto err_register;
 | 
		
	
		
			
			|  | 692 | +
 | 
		
	
		
			
			|  | 693 | +	usb_func_set_drvdata ( func, ecm );
 | 
		
	
		
			
			|  | 694 | +	return 0;
 | 
		
	
		
			
			|  | 695 | +
 | 
		
	
		
			
			|  | 696 | +	unregister_netdev ( netdev );
 | 
		
	
		
			
			|  | 697 | + err_register:
 | 
		
	
		
			
			|  | 698 | + err_fetch_mac:
 | 
		
	
		
			
			|  | 699 | + err_ethernet:
 | 
		
	
		
			
			|  | 700 | + err_bulk_out:
 | 
		
	
		
			
			|  | 701 | + err_bulk_in:
 | 
		
	
		
			
			|  | 702 | + err_interrupt:
 | 
		
	
		
			
			|  | 703 | + err_data:
 | 
		
	
		
			
			|  | 704 | + err_comms:
 | 
		
	
		
			
			|  | 705 | + err_count:
 | 
		
	
		
			
			|  | 706 | +	netdev_nullify ( netdev );
 | 
		
	
		
			
			|  | 707 | +	netdev_put ( netdev );
 | 
		
	
		
			
			|  | 708 | + err_alloc:
 | 
		
	
		
			
			|  | 709 | +	return rc;
 | 
		
	
		
			
			|  | 710 | +}
 | 
		
	
		
			
			|  | 711 | +
 | 
		
	
		
			
			|  | 712 | +/**
 | 
		
	
		
			
			|  | 713 | + * Remove device
 | 
		
	
		
			
			|  | 714 | + *
 | 
		
	
		
			
			|  | 715 | + * @v func		USB function
 | 
		
	
		
			
			|  | 716 | + */
 | 
		
	
		
			
			|  | 717 | +static void ecm_remove ( struct usb_function *func ) {
 | 
		
	
		
			
			|  | 718 | +	struct ecm_device *ecm = usb_func_get_drvdata ( func );
 | 
		
	
		
			
			|  | 719 | +	struct net_device *netdev = ecm->netdev;
 | 
		
	
		
			
			|  | 720 | +
 | 
		
	
		
			
			|  | 721 | +	unregister_netdev ( netdev );
 | 
		
	
		
			
			|  | 722 | +	netdev_nullify ( netdev );
 | 
		
	
		
			
			|  | 723 | +	netdev_put ( netdev );
 | 
		
	
		
			
			|  | 724 | +}
 | 
		
	
		
			
			|  | 725 | +
 | 
		
	
		
			
			|  | 726 | +/** CDC-ECM device IDs */
 | 
		
	
		
			
			|  | 727 | +static struct usb_device_id ecm_ids[] = {
 | 
		
	
		
			
			|  | 728 | +	{
 | 
		
	
		
			
			|  | 729 | +		.name = "cdc-ecm",
 | 
		
	
		
			
			|  | 730 | +		.vendor = USB_ANY_ID,
 | 
		
	
		
			
			|  | 731 | +		.product = USB_ANY_ID,
 | 
		
	
		
			
			|  | 732 | +		.class = {
 | 
		
	
		
			
			|  | 733 | +			.class = USB_CLASS_CDC,
 | 
		
	
		
			
			|  | 734 | +			.subclass = USB_SUBCLASS_CDC_ECM,
 | 
		
	
		
			
			|  | 735 | +			.protocol = 0,
 | 
		
	
		
			
			|  | 736 | +		},
 | 
		
	
		
			
			|  | 737 | +	},
 | 
		
	
		
			
			|  | 738 | +};
 | 
		
	
		
			
			|  | 739 | +
 | 
		
	
		
			
			|  | 740 | +/** CDC-ECM driver */
 | 
		
	
		
			
			|  | 741 | +struct usb_driver ecm_driver __usb_driver = {
 | 
		
	
		
			
			|  | 742 | +	.ids = ecm_ids,
 | 
		
	
		
			
			|  | 743 | +	.id_count = ( sizeof ( ecm_ids ) / sizeof ( ecm_ids[0] ) ),
 | 
		
	
		
			
			|  | 744 | +	.probe = ecm_probe,
 | 
		
	
		
			
			|  | 745 | +	.remove = ecm_remove,
 | 
		
	
		
			
			|  | 746 | +};
 |