1/*  *********************************************************************
2    *  Broadcom Common Firmware Environment (CFE)
3    *
4    *  USB Ethernet				File: dev_usb_rtek.c
5    *
6    *  Driver for USB Ethernet devices using Realtek RTL8150 chip.
7    *
8    *********************************************************************
9    *
10    *  Copyright 2000,2001,2002,2003,2005
11    *  Broadcom Corporation. All rights reserved.
12    *
13    *  This software is furnished under license and may be used and
14    *  copied only in accordance with the following terms and
15    *  conditions.  Subject to these conditions, you may download,
16    *  copy, install, use, modify and distribute modified or unmodified
17    *  copies of this software in source and/or binary form.  No title
18    *  or ownership is transferred hereby.
19    *
20    *  1) Any source code used, modified or distributed must reproduce
21    *     and retain this copyright notice and list of conditions
22    *     as they appear in the source file.
23    *
24    *  2) No right is granted to use any trade name, trademark, or
25    *     logo of Broadcom Corporation.  The "Broadcom Corporation"
26    *     name may not be used to endorse or promote products derived
27    *     from this software without the prior written permission of
28    *     Broadcom Corporation.
29    *
30    *  3) THIS SOFTWARE IS PROVIDED "AS-IS" AND ANY EXPRESS OR
31    *     IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO, ANY IMPLIED
32    *     WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
33    *     PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT
34    *     SHALL BROADCOM BE LIABLE FOR ANY DAMAGES WHATSOEVER, AND IN
35    *     PARTICULAR, BROADCOM SHALL NOT BE LIABLE FOR DIRECT, INDIRECT,
36    *     INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
37    *     (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
38    *     GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
39    *     BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
40    *     OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
41    *     TORT (INCLUDING NEGLIGENCE OR OTHERWISE), EVEN IF ADVISED OF
42    *     THE POSSIBILITY OF SUCH DAMAGE.
43    ********************************************************************* */
44
45/*  *********************************************************************
46    *  USB-Ethernet driver - CFE Network Layer Interfaces
47    ********************************************************************* */
48
49#include "cfe.h"
50
51#include "usbd.h"
52#include "usbeth.h"
53
54#if 0
55#define USBETH_TRACE( x, y ... ) xprintf( x, ##y )
56#else
57#define USBETH_TRACE( x, y ... ) ((void)0)
58#endif
59
60#define FAIL				-1
61
62#define CACHE_ALIGN    32       /* XXX place holder, big enough to now. */
63#define ALIGN(n,align) (((n)+((align)-1)) & ~((align)-1))
64
65#define usb_dma_alloc(n) (KMALLOC(ALIGN((n),CACHE_ALIGN),CACHE_ALIGN))
66#define usb_dma_free(p)  (KFREE(p))
67
68/******************************************************************************
69  Debug functions
70******************************************************************************/
71
72#ifndef USBETH_DEBUG
73#define USBETH_DEBUG 0
74#endif
75
76#if USBETH_DEBUG
77static void hexdump( unsigned char *src, int srclen, int rowlen, int rows )
78{
79    unsigned char *rowptr;
80    unsigned char *srcstp;
81    unsigned char *byteptr;
82
83    srcstp = src + srclen;
84
85    for( rowptr = src; rowptr < src + rowlen * rows; rowptr += rowlen ) {
86	for( byteptr = rowptr; byteptr < rowptr + rowlen && byteptr < srcstp; byteptr++ ) {
87	    xprintf( "%2X ", *byteptr );
88	    }
89	xprintf( "\n" );
90	}
91    xprintf( "\n" );
92}
93#else
94#define hexdump(src,srclen,rowlen,rows) ((void)0)
95#endif
96
97
98/*  *********************************************************************
99    * Interface functions for USB-Ethernet adapters
100    ********************************************************************* */
101
102enum { VEN_NONE, LINKSYS_100M };
103static const char *VENDOR_NAMES[] = {
104    "?", "Linksys-100M", "Yikes!"
105};
106
107static const int ID_TBL[] = {
108    0x0BDA, 0x8150, LINKSYS_100M,
109    -1
110};
111
112typedef struct rtek_softc_s {
113    usbdev_t *dev;
114    int bulk_inpipe;
115    int bulk_outpipe;
116    int ven_code;
117    uint8_t mac_addr[6];
118    usbreq_t *rx_ur;
119    uint8_t rxbuf[1600];    /* arbitrary but enough for ethernet packet */
120} rtek_softc_t;
121
122
123/* **************************************
124   * REALTEK RTL8150 I/F Functions.
125   ************************************** */
126
127static int rtek_send_eth_frame( void *ctx, hsaddr_t buf, int len );
128static int rtek_get_eth_frame( void *ctx, hsaddr_t buf );
129static int rtek_data_rx( void *ctx );
130static int rtek_get_dev_addr( void *ctx, hsaddr_t mac_addr );
131
132static usbeth_disp_t usbeth_rtek = {
133    rtek_get_eth_frame,
134    rtek_data_rx,
135    rtek_send_eth_frame,
136    rtek_get_dev_addr
137};
138
139
140static int rtek_get_reg( usbdev_t *dev, int16_t reg, uint8_t *val, int16_t len )
141{
142    return usb_std_request( dev, (USBREQ_TYPE_VENDOR | USBREQ_DIR_IN),
143			    RTEK_REG_ACCESS, reg, 0, val, len );
144}
145
146static int rtek_set_reg( usbdev_t *dev, int16_t reg, int8_t val )
147{
148    uint8_t data[1];
149
150    data[0] = val;
151    return usb_std_request( dev, (USBREQ_TYPE_VENDOR | USBREQ_DIR_OUT),
152			    RTEK_REG_ACCESS, reg, 0, data, 1 );
153}
154
155static int rtek_get_mac_addr( usbdev_t *dev, uint8_t *mac_addr )
156{
157    return rtek_get_reg( dev, R_RTEK_MAC, mac_addr, 6 );
158}
159
160static int rtek_init_device( rtek_softc_t *softc )
161{
162    usb_device_descr_t dev_desc;
163    uint16_t vendor_id, product_id;
164    const int *ptr=ID_TBL;
165    int i;
166    usbdev_t *dev = softc->dev;
167    uint8_t val;
168
169    /* find out which device is connected */
170    usb_get_device_descriptor( softc->dev, &dev_desc, 0 );
171    vendor_id = (dev_desc.idVendorHigh  << 8) + dev_desc.idVendorLow;
172    product_id = (dev_desc.idProductHigh << 8) + dev_desc.idProductLow;
173
174    while( *ptr != -1 )	{
175	if( (vendor_id == ptr[0]) && (product_id == ptr[1]) ) {
176	    softc->ven_code = ptr[2];
177	    break;
178	    }
179	ptr += 3;
180	}
181    if( *ptr == -1 ) {
182	xprintf( "Unrecognized Realtek USB-Ethernet device\n" );
183	return -1;
184	}
185
186    /* Reset the adapter */
187    rtek_set_reg( dev, R_RTEK_CMD, RTEK_RESET );
188    for( i = 0; i < 10; ++i ) {
189	rtek_get_reg( dev, R_RTEK_CMD, &val, 1 );
190	if( !(val & RTEK_RESET) )
191	    break;
192	usb_delay_ms( NULL, 1 );
193	}
194
195    /* Autoload the internal registers */
196    rtek_set_reg( dev, R_RTEK_CMD, RTEK_AUTOLOAD );
197    for( i = 0; i < 50; ++i ) {
198	rtek_get_reg( dev, R_RTEK_CMD, &val, 1 );
199	if( !(val & RTEK_AUTOLOAD) )
200	    break;
201	usb_delay_ms( NULL, 1 );
202	}
203
204    /* Read the adapter's MAC addr */
205    rtek_get_mac_addr( dev, softc->mac_addr );
206
207    /* display adapter info */
208    xprintf( "%s USB-Ethernet Adapter (%a)\n",
209	     VENDOR_NAMES[softc->ven_code], softc->mac_addr);
210
211    return 0;
212}
213
214static int rtek_get_dev_addr( void *ctx, hsaddr_t mac_addr )
215{
216    rtek_softc_t *softc = (rtek_softc_t *) ctx;
217    hs_memcpy_to_hs( mac_addr, softc->mac_addr, 6 );
218    return 0;
219}
220
221static void rtek_queue_rx( rtek_softc_t *softc )
222{
223    softc->rx_ur = usb_make_request(softc->dev, softc->bulk_inpipe,
224				    softc->rxbuf, sizeof(softc->rxbuf),
225				    (UR_FLAG_IN | UR_FLAG_SHORTOK));
226    usb_queue_request(softc->rx_ur);
227}
228
229static int rtek_data_rx( void *ctx )
230{
231    rtek_softc_t *softc = (rtek_softc_t *) ctx;
232    usb_poll(softc->dev->ud_bus);
233    return( !softc->rx_ur->ur_inprogress );
234}
235
236static int rtek_get_eth_frame( void *ctx, hsaddr_t buf )
237{
238    int len = 0;
239    rtek_softc_t *softc = (rtek_softc_t *) ctx;
240    uint8_t *rxbuf;
241
242    if( !softc->rx_ur->ur_inprogress ) {
243	rxbuf = softc->rxbuf;
244	len = softc->rx_ur->ur_xferred;
245	if (len > 0) {
246#if USBETH_DEBUG
247	    xprintf( "Incoming packet :\n" );
248	    hexdump( rxbuf, len, 16, len / 16 + 1 );
249#endif
250	    hs_memcpy_to_hs( buf, rxbuf, len );
251	    }
252	usb_free_request(softc->rx_ur);
253	rtek_queue_rx( softc );
254	}
255    else
256	xprintf( "Bulk data is not available yet!\n" );
257
258    return( len );
259}
260
261static int rtek_send_eth_frame( void *ctx, hsaddr_t buf, int len )
262{
263    rtek_softc_t *softc = (rtek_softc_t *) ctx;
264    usbreq_t *ur;
265    int txlen = len;
266    unsigned char *txbuf;
267
268    /* First some Realtek chip workarounds */
269    if( txlen < 60 )		/* some strange limitation */
270	txlen = 60;
271    else if( !(txlen % 64) )	/* to handle module 64 packets */
272	++txlen;
273
274    txbuf = usb_dma_alloc(txlen);
275    hs_memcpy_from_hs( txbuf, buf, txlen );
276
277#if USBETH_DEBUG
278    xprintf( "Outgoing packet :\n" );
279    hexdump( txbuf, txlen, 16, txlen / 16 + 1 );
280#endif
281    ur = usb_make_request(softc->dev, softc->bulk_outpipe,
282	                      txbuf, txlen, UR_FLAG_OUT);
283    usb_sync_request(ur);
284    usb_free_request(ur);
285    usb_dma_free(txbuf);
286
287    return( len );
288}
289
290static void rtek_open_device( rtek_softc_t *softc )
291{
292    /* Accept broadcast & own packets */
293    rtek_set_reg( softc->dev, R_RTEK_RXCFG, RTEK_MACADDR | RTEK_BCASTADDR );
294
295    /* Enable adapter to receive and transmit packets */
296    rtek_set_reg( softc->dev, R_RTEK_CMD, RTEK_RXENABLE | RTEK_TXENABLE );
297
298    /* kick start the receive */
299    rtek_queue_rx( softc );
300}
301
302
303static void rtek_close_device( rtek_softc_t *softc )
304{
305    usbdev_t *dev = softc->dev;
306
307    /* Disable adapter from receiving or transmitting packets */
308    rtek_set_reg( dev, R_RTEK_CMD, 0 );
309}
310
311
312/*  *********************************************************************
313    * CFE-USB interfaces
314    ********************************************************************* */
315
316/*  *********************************************************************
317    *  rtek_attach(dev,drv)
318    *
319    *  This routine is called when the bus scan stuff finds a usb-ethernet
320    *  device.  We finish up the initialization by configuring the
321    *  device and allocating our softc here.
322    *
323    *  Input parameters:
324    *      dev - usb device, in the "addressed" state.
325    *      drv - the driver table entry that matched
326    *
327    *  Return value:
328    *      0
329    ********************************************************************* */
330
331const cfe_driver_t usbrtekdrv;		/* forward declaration */
332
333static int rtek_attach(usbdev_t *dev, usb_driver_t *drv)
334{
335    usb_config_descr_t *cfgdscr = dev->ud_cfgdescr;
336    usb_endpoint_descr_t *epdscr;
337    usb_endpoint_descr_t *indscr = NULL;
338    usb_endpoint_descr_t *outdscr = NULL;
339    usb_interface_descr_t *ifdscr;
340    rtek_softc_t *softc;
341    int idx;
342
343    dev->ud_drv = drv;
344
345    softc = (rtek_softc_t *) KMALLOC( sizeof(rtek_softc_t), 0 );
346    if( softc == NULL )	{
347	xprintf( "Failed to allocate softc memory.\n" );
348	return -1;
349	}
350    memset( softc, 0, sizeof(rtek_softc_t) );
351    dev->ud_private = softc;
352    softc->dev = dev;
353
354    ifdscr = usb_find_cfg_descr(dev,USB_INTERFACE_DESCRIPTOR_TYPE,0);
355    if (ifdscr == NULL) {
356	xprintf("USBETH: ERROR...no interace descriptor\n");
357	return -1;
358	}
359
360    for (idx = 0; idx < 2; idx++) {
361	epdscr = usb_find_cfg_descr(dev,USB_ENDPOINT_DESCRIPTOR_TYPE,idx);
362	if (USB_ENDPOINT_DIR_OUT(epdscr->bEndpointAddress))
363	    outdscr = epdscr;
364	else
365	    indscr = epdscr;
366	}
367
368    if (!indscr || !outdscr) {
369	/*
370	 * Could not get descriptors, something is very wrong.
371	 * Leave device addressed but not configured.
372	 */
373	xprintf("USBETH: ERROR...no endpoint descriptors\n");
374	return -1;
375	}
376
377    /* Choose the standard configuration. */
378    usb_set_configuration(dev,cfgdscr->bConfigurationValue);
379
380    /* Quit if not able to initialize the device */
381    if (rtek_init_device(softc) < 0)
382	return -1;
383
384    /* Open the pipes. */
385    softc->bulk_inpipe     = usb_open_pipe(dev,indscr);
386    softc->bulk_outpipe    = usb_open_pipe(dev,outdscr);
387
388    /* Register the device */
389    usbeth_register(&usbeth_rtek,softc);
390
391    /* Open the device */
392    rtek_open_device( softc );
393
394    return 0;
395}
396
397/*  *********************************************************************
398    *  rtek_detach(dev)
399    *
400    *  This routine is called when the bus scanner notices that
401    *  this device has been removed from the system.  We should
402    *  do any cleanup that is required.  The pending requests
403    *  will be cancelled automagically.
404    *
405    *  Input parameters:
406    *      dev - usb device
407    *
408    *  Return value:
409    *      0
410    ********************************************************************* */
411
412static int rtek_detach(usbdev_t *dev)
413{
414    rtek_softc_t *softc = (rtek_softc_t *) dev->ud_private;
415
416    if (softc != NULL) {
417	usbeth_unregister( softc );
418	rtek_close_device( softc );
419	dev->ud_private = NULL;
420	softc->dev = NULL;
421	KFREE(softc);
422	}
423
424    return 0;
425}
426
427/*  CFE USB device interface structure */
428usb_driver_t usbrtek_driver =
429{
430    "Ethernet Device",
431    rtek_attach,
432    rtek_detach
433};
434
435
436
437
438