1/*  *********************************************************************
2    *  Broadcom Common Firmware Environment (CFE)
3    *
4    *  USB Ethernet				File: dev_usb_catc.c
5    *
6    *  Driver for USB Ethernet devices using the CATC Netmate 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, CATC_NM, BELKIN_CATC };
103static const char *VENDOR_NAMES[] = {
104    "?", "CATC-Netmate", "Belkin/CATC", "Yikes!"
105};
106
107static const int ID_TBL[] = {
108    0x0423, 0x000a, CATC_NM,		/* CATC (Netmate I) */
109    0x0423, 0x000c, BELKIN_CATC,	/* Belkin/CATC (Netmate II) */
110    -1
111};
112
113typedef struct catc_softc_s {
114    usbdev_t *dev;
115    int bulk_inpipe;
116    int bulk_outpipe;
117    int ven_code;
118    uint8_t mac_addr[6];
119    usbreq_t *rx_ur;
120    uint8_t rxbuf[1600];    /* arbitrary but enough for ethernet packet */
121} catc_softc_t;
122
123
124/* **************************************
125   *  CATC I/F Functions
126   ************************************** */
127
128static int catc_send_eth_frame( void *ctx, hsaddr_t buf, int len );
129static int catc_get_eth_frame( void *ctx, hsaddr_t buf );
130static int catc_data_rx( void *ctx );
131static int catc_get_dev_addr( void *ctx, hsaddr_t mac_addr );
132
133static usbeth_disp_t usbeth_catc = {
134    catc_get_eth_frame,
135    catc_data_rx,
136    catc_send_eth_frame,
137    catc_get_dev_addr
138};
139
140#if 0
141static int catc_get_reg( usbdev_t *dev, int16_t reg, uint8_t *val )
142{
143    return usb_std_request( dev, (USBREQ_TYPE_VENDOR | USBREQ_DIR_IN),
144			    CATC_GET_REG, 0, reg, val, 1 );
145}
146#endif
147
148static int catc_set_reg( usbdev_t *dev, int16_t reg, int16_t val )
149{
150    return usb_std_request( dev, (USBREQ_TYPE_VENDOR | USBREQ_DIR_OUT),
151			    CATC_SET_REG, val, reg, NULL, 0 );
152}
153
154static int catc_set_mem( usbdev_t *dev, int16_t addr,
155			 uint8_t *data, int16_t len )
156{
157    return usb_std_request( dev, (USBREQ_TYPE_VENDOR | USBREQ_DIR_OUT),
158			    CATC_SET_MEM, 0, addr, data, len );
159}
160
161static int catc_get_mac_addr( usbdev_t *dev, uint8_t *mac_addr )
162{
163    return usb_std_request( dev, (USBREQ_TYPE_VENDOR | USBREQ_DIR_IN),
164			      CATC_GET_MAC_ADDR, 0, 0, mac_addr, 6 );
165}
166
167static int catc_init_device( catc_softc_t *softc )
168{
169    usb_device_descr_t dev_desc;
170    uint16_t vendor_id, product_id;
171    const int *ptr=ID_TBL;
172    usbdev_t *dev = softc->dev;
173    unsigned char *mcast_tbl;
174
175
176    /* find out which device is connected */
177    usb_get_device_descriptor( softc->dev, &dev_desc, 0 );
178    vendor_id = (dev_desc.idVendorHigh  << 8) + dev_desc.idVendorLow;
179    product_id = (dev_desc.idProductHigh << 8) + dev_desc.idProductLow;
180
181    while( *ptr != -1 )	{
182	if( (vendor_id == ptr[0]) && (product_id == ptr[1]) ) {
183	    softc->ven_code = ptr[2];
184	    break;
185	    }
186	ptr += 3;
187	}
188    if( *ptr == -1 ) {
189	xprintf( "Unrecognized CATC USB-Ethernet device\n" );
190	return -1;
191	}
192
193    usb_std_request( dev, (USBREQ_TYPE_STD | USBREQ_REC_INTERFACE),
194		     USB_REQUEST_SET_INTERFACE,
195		     1,         /* alt setting 1 */
196		     0, NULL, 0 );
197
198    catc_set_reg(dev, CATC_TX_BUF_CNT_REG, 0x04 );
199    catc_set_reg(dev, CATC_RX_BUF_CNT_REG, 0x10 );
200    catc_set_reg(dev, CATC_ADV_OP_MODES_REG, 0x01 );
201    catc_set_reg(dev, CATC_LED_CTRL_REG, 0x08 );
202
203    /* Enable broadcast rx via bit in multicast table */
204    mcast_tbl = KMALLOC(64, CACHE_ALIGN);
205    memset( mcast_tbl, 0, 64 );
206    mcast_tbl[31] = 0x80;     /* broadcast bit */
207    catc_set_mem( dev, CATC_MCAST_TBL_ADDR, mcast_tbl, 64 );
208    KFREE(mcast_tbl);
209
210    /* Read the adapter's MAC addr */
211    catc_get_mac_addr( dev, softc->mac_addr );
212
213    /* display adapter info */
214    xprintf( "%s USB-Ethernet Adapter (%a)\n",
215	     VENDOR_NAMES[softc->ven_code], softc->mac_addr);
216
217    return 0;
218}
219
220static int catc_get_dev_addr( void *ctx, hsaddr_t mac_addr )
221{
222    catc_softc_t *softc = (catc_softc_t *) ctx;
223    hs_memcpy_to_hs( mac_addr, softc->mac_addr, 6 );
224    return 0;
225}
226
227static void catc_queue_rx( catc_softc_t *softc )
228{
229    softc->rx_ur = usb_make_request(softc->dev, softc->bulk_inpipe,
230				    softc->rxbuf, sizeof(softc->rxbuf),
231				    (UR_FLAG_IN | UR_FLAG_SHORTOK));
232    usb_queue_request(softc->rx_ur);
233}
234
235static int catc_data_rx( void *ctx )
236{
237    catc_softc_t *softc = (catc_softc_t *) ctx;
238    usb_poll(softc->dev->ud_bus);
239    return( !softc->rx_ur->ur_inprogress );
240}
241
242static int catc_get_eth_frame( void  *ctx, hsaddr_t buf )
243{
244    catc_softc_t *softc = (catc_softc_t *) ctx;
245    int len = 0;
246    uint8_t *rxbuf;
247
248    if( !softc->rx_ur->ur_inprogress ) {
249	rxbuf = softc->rxbuf;
250	len = softc->rx_ur->ur_xferred;
251	if (len > 0) {
252#if CATC_DEBUG
253	    xprintf( "Incoming packet :\n" );
254	    hexdump( rxbuf, len, 16, len / 16 + 1 );
255#endif
256	    hs_memcpy_to_hs( buf, rxbuf, len );
257	    }
258	usb_free_request(softc->rx_ur);
259	catc_queue_rx( softc );
260	}
261    else
262	xprintf( "Bulk data is not available yet!\n" );
263
264    return( len );
265}
266
267static int catc_send_eth_frame( void *ctx, hsaddr_t buf, int len )
268{
269    catc_softc_t *softc = (catc_softc_t *) ctx;
270    usbreq_t *ur;
271    int txlen = len;
272    unsigned char *txbuf;
273
274    txbuf = usb_dma_alloc(len+2);
275    txbuf[0] = txlen & 0xff;
276    txbuf[1] = (txlen >> 8) & 0xff;   /* 1st two bytes...little endian */
277    hs_memcpy_from_hs( &txbuf[2], buf, txlen );
278    txlen += 2;
279#if USBETH_DEBUG
280    xprintf( "Outgoing packet :\n" );
281    hexdump( txbuf, txlen, 16, txlen / 16 + 1 );
282#endif
283    ur = usb_make_request(softc->dev, softc->bulk_outpipe,
284	                      txbuf, txlen, UR_FLAG_OUT);
285    usb_sync_request(ur);
286    usb_free_request(ur);
287    usb_dma_free(txbuf);
288
289    return( len );
290}
291
292static void catc_open_device( catc_softc_t *softc )
293{
294    int i;
295
296    for(i = 0; i < 6; ++i)
297	catc_set_reg( softc->dev, (CATC_ETH_ADDR_0_REG - i), softc->mac_addr[i] );
298
299    /* Enable adapter to receive packets */
300    catc_set_reg( softc->dev, CATC_ETH_CTRL_REG, 0x09 );
301
302    /* kick start the receive */
303    catc_queue_rx( softc );
304}
305
306static void catc_close_device( catc_softc_t *softc )
307{
308    usbdev_t *dev = softc->dev;
309
310    /* Disable adapter from receiving packets */
311    catc_set_reg( dev, CATC_ETH_CTRL_REG, 0 );
312}
313
314
315/*  *********************************************************************
316    * CFE-USB interfaces
317    ********************************************************************* */
318
319/*  *********************************************************************
320    *  catc_attach(dev,drv)
321    *
322    *  This routine is called when the bus scan stuff finds a usb-ethernet
323    *  device.  We finish up the initialization by configuring the
324    *  device and allocating our softc here.
325    *
326    *  Input parameters:
327    *      dev - usb device, in the "addressed" state.
328    *      drv - the driver table entry that matched
329    *
330    *  Return value:
331    *      0
332    ********************************************************************* */
333
334const cfe_driver_t usbcatcdrv;		/* forward declaration */
335
336static int catc_attach(usbdev_t *dev, usb_driver_t *drv)
337{
338    usb_config_descr_t *cfgdscr = dev->ud_cfgdescr;
339    usb_endpoint_descr_t *epdscr;
340    usb_endpoint_descr_t *indscr = NULL;
341    usb_endpoint_descr_t *outdscr = NULL;
342    usb_interface_descr_t *ifdscr;
343    catc_softc_t *softc;
344    int idx;
345
346    dev->ud_drv = drv;
347
348    softc = (catc_softc_t *) KMALLOC( sizeof(catc_softc_t), 0 );
349    if( softc == NULL )	{
350	xprintf( "Failed to allocate softc memory.\n" );
351	return -1;
352	}
353    memset( softc, 0, sizeof(catc_softc_t) );
354    dev->ud_private = softc;
355    softc->dev = dev;
356
357    ifdscr = usb_find_cfg_descr(dev,USB_INTERFACE_DESCRIPTOR_TYPE,0);
358    if (ifdscr == NULL) {
359	xprintf("USBETH: ERROR...no interace descriptor\n");
360	return -1;
361	}
362
363    for (idx = 0; idx < 2; idx++) {
364	epdscr = usb_find_cfg_descr(dev,USB_ENDPOINT_DESCRIPTOR_TYPE,idx);
365	if (USB_ENDPOINT_DIR_OUT(epdscr->bEndpointAddress))
366	    outdscr = epdscr;
367	else
368	    indscr = epdscr;
369	}
370
371    if (!indscr || !outdscr) {
372	/*
373	 * Could not get descriptors, something is very wrong.
374	 * Leave device addressed but not configured.
375	 */
376	xprintf("USBETH: ERROR...no endpoint descriptors\n");
377	return -1;
378	}
379
380    /* Choose the standard configuration. */
381    usb_set_configuration(dev,cfgdscr->bConfigurationValue);
382
383    /* Quit if not able to initialize the device */
384    if (catc_init_device(softc) < 0)
385	return -1;
386
387    /* Open the pipes. */
388    softc->bulk_inpipe     = usb_open_pipe(dev,indscr);
389    softc->bulk_outpipe    = usb_open_pipe(dev,outdscr);
390
391    /* Register the device */
392    usbeth_register(&usbeth_catc,softc);
393
394    /* Open the device */
395    catc_open_device( softc );
396
397    return 0;
398}
399
400/*  *********************************************************************
401    *  catc_detach(dev)
402    *
403    *  This routine is called when the bus scanner notices that
404    *  this device has been removed from the system.  We should
405    *  do any cleanup that is required.  The pending requests
406    *  will be cancelled automagically.
407    *
408    *  Input parameters:
409    *      dev - usb device
410    *
411    *  Return value:
412    *      0
413    ********************************************************************* */
414
415static int catc_detach(usbdev_t *dev)
416{
417    catc_softc_t *softc = (catc_softc_t *) dev->ud_private;
418
419    if (softc != NULL) {
420	usbeth_unregister( softc );
421	catc_close_device ( softc );
422	dev->ud_private = NULL;
423	softc->dev = NULL;
424	KFREE(softc);
425	}
426
427    return 0;
428}
429
430/*  CFE USB device interface structure */
431usb_driver_t usbcatc_driver =
432{
433    "Ethernet Device",
434    catc_attach,
435    catc_detach
436};
437
438
439/*  *********************************************************************
440    * CFE-Ethernet device interfaces
441    ********************************************************************* */
442
443
444static int catc_ether_open(cfe_devctx_t *ctx)
445{
446    catc_softc_t *softc = (catc_softc_t *) ctx->dev_softc;
447
448    if (softc->dev == NULL)
449	return CFE_ERR_NOTREADY;
450
451    USBETH_TRACE( "%s called.\n", __FUNCTION__ );
452    catc_open_device( softc );
453
454    return 0;
455}
456
457static int catc_ether_read( cfe_devctx_t *ctx, iocb_buffer_t *buffer )
458{
459    catc_softc_t *softc = (catc_softc_t *) ctx->dev_softc;
460
461    if (softc->dev == NULL)
462	return CFE_ERR_NOTREADY;
463
464    buffer->buf_retlen = catc_get_eth_frame( softc, buffer->buf_ptr );
465
466    return 0;
467}
468
469
470static int catc_ether_inpstat( cfe_devctx_t *ctx, iocb_inpstat_t *inpstat )
471{
472    catc_softc_t *softc = (catc_softc_t *) ctx->dev_softc;
473
474    if (softc->dev == NULL)
475	return CFE_ERR_NOTREADY;
476
477    inpstat->inp_status = catc_data_rx( softc );
478    return 0;
479}
480
481
482static int catc_ether_write(cfe_devctx_t *ctx,iocb_buffer_t *buffer)
483{
484    catc_softc_t *softc = (catc_softc_t *) ctx->dev_softc;
485
486    if (softc->dev == NULL)
487	return CFE_ERR_NOTREADY;
488
489    /* Block until hw notifies you data is sent. */
490    catc_send_eth_frame( softc, buffer->buf_ptr, buffer->buf_length );
491
492    return 0;
493}
494
495
496static int catc_ether_ioctl(cfe_devctx_t *ctx,iocb_buffer_t *buffer)
497{
498    catc_softc_t *softc = (catc_softc_t *) ctx->dev_softc;
499    int retval = 0;
500
501    if (softc->dev == NULL)
502	return CFE_ERR_NOTREADY;
503
504    switch( (int)buffer->buf_ioctlcmd ) {
505	case IOCTL_ETHER_GETHWADDR:
506	    USBETH_TRACE( "IOCTL_ETHER_GETHWADDR called.\n" );
507	    catc_get_dev_addr( softc, buffer->buf_ptr );
508	    break;
509	case IOCTL_ETHER_SETHWADDR:
510	    xprintf( "IOCTL_ETHER_SETHWADDR not implemented.\n" );
511	    break;
512#if 0
513	case IOCTL_ETHER_GETSPEED:
514	    xprintf( "GETSPEED not implemented.\n" );
515	    retval = -1;
516	    break;
517	case IOCTL_ETHER_SETSPEED:
518	    xprintf( "SETSPEED not implemented.\n" );
519	    retval = -1;
520	    break;
521	case IOCTL_ETHER_GETLINK:
522	    xprintf( "GETLINK not implemented.\n" );
523	    retval = -1;
524	    break;
525	case IOCTL_ETHER_GETLOOPBACK:
526	    xprintf( "GETLOOPBACK not implemented.\n" );
527	    retval = -1;
528	    break;
529	case IOCTL_ETHER_SETLOOPBACK:
530	    xprintf( "SETLOOPBACK not implemented.\n" );
531	    retval = -1;
532	    break;
533#endif
534	default:
535	    xprintf( "Invalid IOCTL to catc_ether_ioctl.\n" );
536	    retval = -1;
537	}
538
539    return retval;
540}
541
542
543static int catc_ether_close(cfe_devctx_t *ctx)
544{
545    catc_softc_t *softc = (catc_softc_t *) ctx->dev_softc;
546
547    if (softc->dev == NULL)
548	return CFE_ERR_NOTREADY;
549
550    USBETH_TRACE( "%s called.\n", __FUNCTION__ );
551    catc_close_device( softc );
552    return 0;
553}
554
555
556/* CFE ethernet device interface structures */
557
558const static cfe_devdisp_t catc_ether_dispatch =
559{
560    catc_ether_open,
561    catc_ether_read,
562    catc_ether_inpstat,
563    catc_ether_write,
564    catc_ether_ioctl,
565    catc_ether_close,
566    NULL,
567    NULL
568};
569
570const cfe_driver_t usbcatcdrv =
571{
572    "USB-Ethernet Device",
573    "eth",
574    CFE_DEV_NETWORK,
575    &catc_ether_dispatch,
576    NULL,			/* probe...not needed */
577};
578
579