source: libbsdport/bsd_eth_drivers/libbsdport/devicet.c @ 831a437

Last change on this file since 831a437 was 831a437, checked in by Till Straumann <strauman@…>, on Apr 23, 2009 at 4:36:41 AM
  • make entries in default driver table all weak aliases to a sentinel. If the user decides only to link a subset of driver the probe algorithm skips entries matching the sentinel.
  • added more diagnostic messages which can be enabled by setting 'libbsdportAttachVerbose' to nonzero. This is quite helpful.
  • added the ugliest of all hack -- i386 pcibios API returns wrong number for bus count :-( [the max bus number instead of the count]. We simply add 1 to this number...
  • Property mode set to 100644
File size: 8.5 KB
Line 
1#define DEVICET_EXTERN_INLINE
2
3#include "devicet.h"
4#include <rtems/rtems_bsdnet.h>
5#include <sys/malloc.h>
6#include <assert.h>
7#include <ctype.h>
8#include <string.h>
9#include <stdio.h>
10#include <rtems/pci.h>
11#include <rtems/error.h>
12#include <sys/bus.h>
13#include "libbsdport_api.h"
14
15<<<<<<< devicet.c
16#define  DEBUG 0
17int libbsdportAttachVerbose = DEBUG;
18=======
19#undef  DEBUG
20>>>>>>> 1.1.1.2
21
22extern void real_libc_free(void*);
23
24static STAILQ_HEAD(devq_t, device) devq = STAILQ_HEAD_INITIALIZER(devq);
25
26static device_t
27devalloc(driver_t *dr)
28{
29void     *m;
30device_t rval;
31int      l = sizeof(*rval) + dr->softc_size + DEVICE_SOFTC_ALIGNMENT -1;
32
33        if ( !(m = malloc( l, M_DEVBUF, M_NOWAIT )) )
34                return 0;
35
36        memset(m, 0, l);
37
38        rval = (device_t)(((uintptr_t)m + (DEVICE_SOFTC_ALIGNMENT-1)) & ~(DEVICE_SOFTC_ALIGNMENT-1));
39        rval->rawmem = m;
40        rval->type   = dr->type;
41        rval->name   = dr->name;
42        rval->drv    = dr; 
43
44        return rval;
45}
46
47static void
48devclean(device_t dev)
49{
50        assert( !dev->attached );
51        memset(device_get_softc(dev), 0, dev->drv->softc_size);
52        real_libc_free(dev->desc);
53        dev->desc = 0;
54        dev->unit = 0;
55        dev->nameunit[0]=0;
56        memset( &dev->bushdr, 0, sizeof(dev->bushdr));
57}
58
59static void
60devfree(device_t dev)
61{
62        /* paranoia */
63        devclean(dev);
64        dev->drv = 0;
65        free(dev->rawmem, M_DEVBUF);
66}
67
68static int
69devattach(device_t dev, int unit, struct rtems_bsdnet_ifconfig *cfg)
70{
71int error;
72
73        if ( libbsdportAttachVerbose ) {
74                printf("Now attaching %s%d: (0x%x:%x.%x)\n",
75                        dev->name, unit, 
76                        dev->bushdr.pci.bus, dev->bushdr.pci.dev, dev->bushdr.pci.fun);
77        }
78
79        dev->unit     = unit;
80        dev->ifconfig = cfg;
81        sprintf(dev->nameunit,"%s%d",dev->drv->name,unit);
82
83        /* Try to attach */
84        if ( (error = dev->drv->methods->attach(dev)) ) {
85                fprintf(stderr,"Attaching '%s%d' failed: %s", dev->drv->name, unit, strerror(error));
86                return error;
87        }
88        /* Successfully attached new device */
89        dev->attached = 1;
90        cfg->name     = (char*)device_get_nameunit(dev);
91        STAILQ_INSERT_TAIL(&devq, dev, list);
92        return 0;
93}
94
95static int
96devequal(device_t a, device_t b)
97{
98        if ( a->type != b->type )
99                return 0;
100        switch ( a->type ) {
101                case DEV_TYPE_PCI:
102                        return  a->bushdr.pci.bus == b->bushdr.pci.bus
103                             && a->bushdr.pci.dev == b->bushdr.pci.dev
104                             && a->bushdr.pci.fun == b->bushdr.pci.fun;
105
106                default:
107                        rtems_panic("devequal: Unsupported device type %i\n", a->type);
108        }
109        return 0;
110}
111
112/* Check if a particular device is already listed */
113static device_t
114devattached(device_t dev)
115{
116struct device *ldev;
117        STAILQ_FOREACH(ldev, &devq, list) {
118                if ( devequal(ldev, dev) )
119                        return ldev;
120        }
121        return 0;
122}
123
124
125int
126device_printf(device_t dev, const char *fmt, ...)
127{
128int rval;
129va_list ap;
130        rval  = fprintf(stdout,"%s:",device_get_nameunit(dev));
131        va_start(ap, fmt);
132        rval += vfprintf(stdout,fmt,ap);
133        va_end(ap);
134        return rval;
135}
136
137static uint32_t
138get_pci_triple(const char *drvnam)
139{
140unsigned b,d,f;
141        if ( drvnam && 3 == sscanf(drvnam,"%i:%i.%i",&b,&d,&f) )
142                return (b<<8) | PCI_DEVFN(d,f);
143        return -1;
144}
145
146static void
147get_name_unit(const char *drvnam, char *nm, int *punit)
148{
149int l = strlen(drvnam);
150int i;
151        if ( l > 0 ) {
152                for ( i=l-1; i>=0 && isdigit(drvnam[i]); i-- )
153                        /* nothing else to do */;
154                if ( 1 != sscanf(drvnam+i,"%d",punit) )
155                        *punit = 0; /* wildcard */
156                strncpy(nm, drvnam, i+1);
157                nm[i+1]=0;
158        } else {
159                /* wildcards */
160                *nm    = 0;
161                *punit = 0;
162        }
163}
164
165static int
166matches(driver_t *dr, const char *pat)
167{
168        if ( 0 == *pat || '*' == *pat )
169                return 1;
170        return !strcmp(pat, dr->name);
171}
172
173static int
174pci_slot_empty(int b, int d, int f)
175{
176uint16_t id;
177        pci_read_config_word(b,d,f,PCI_VENDOR_ID,&id);
178        return ( 0xffff == id );
179}
180
181static int
182pci_is_ether(int b, int d, int f)
183{
184uint16_t dclass;
185        if ( pci_slot_empty(b,d,f) )
186                return 0;
187        pci_read_config_word(b,d,f,PCI_CLASS_DEVICE, &dclass);
188        return PCI_CLASS_NETWORK_ETHERNET == dclass;
189}
190
191/* this catches the case of an unpopulated slot (returning 0) */
192static int
193pci_num_functions(int b, int d)
194{
195uint8_t h;
196        if ( pci_slot_empty(b,d,0) )
197                return 0;
198        pci_read_config_byte(b,d,0,PCI_HEADER_TYPE,&h);
199        return (h & 0x80) ? PCI_MAX_FUNCTIONS : 1; /* multifunction device ? */
200}
201
202int
203libbsdport_netdriver_dump(FILE *f)
204{
205struct device *ldev;
206int           ndevs;
207unsigned      w;
208
209        if ( !f )
210                f = stdout;
211
212        ndevs = 0;
213        fprintf(f, "PCI Network device information:\n");
214        rtems_bsdnet_semaphore_obtain();
215        STAILQ_FOREACH(ldev, &devq, list) {
216                /* ASSUME LIST ELEMENTS DO NOT DISAPPEAR
217                 * so we can release the lock while printing...
218                 */
219                rtems_bsdnet_semaphore_release();
220                w=fprintf(f,"%-6s -- (0x%x:%x.%x)",
221                                device_get_nameunit(ldev),
222                                ldev->bushdr.pci.bus,
223                                ldev->bushdr.pci.dev,
224                                ldev->bushdr.pci.fun);
225                for ( ; w < 24 ; w++)
226                        fputc(' ',f);
227                if ( ldev->desc )
228                        fprintf(f," %s",ldev->desc);
229                fputc('\n',f);
230
231
232                ndevs++;
233                rtems_bsdnet_semaphore_obtain();
234        }
235        rtems_bsdnet_semaphore_release();
236        return ndevs;
237}
238
239#define UNITMATCH(wanted, unit, bdfunit) \
240        ((wanted) < 0 ? ((wanted) & 0xffff) == (bdfunit) : (wanted) == (unit))
241
242int
243libbsdport_netdriver_attach(struct rtems_bsdnet_ifconfig *cfg, int attaching)
244{
245char     nm[20]; /* copy of the name */
246int      unit,thisunit,wantedunit;
247int      i,b,d,f;
248int      prob;
249driver_t *dr;
250device_t dev   = 0;
251device_t tmpdev;
252int      error = 0;
253int      bdfunit;
254
255int      n_bus;
256
257                if ( !attaching )
258                        return ENOTSUP;
259
260                if ( (wantedunit = get_pci_triple(cfg->name)) < 0 ) {
261                        get_name_unit(cfg->name, nm, &wantedunit);
262                } else {
263                        wantedunit |= 1<<31;
264                        nm[0]=0;
265                }
266                if ( libbsdportAttachVerbose )
267                        printf("Wanted unit is 0x%x, pattern '%s'\n", wantedunit, nm);
268
269                n_bus = pci_bus_count();
270#ifdef __i386__
271                /* ugliest of all hacks -- RTEMS routine is currently (4.9)
272         * still broken; it reports the (0-based) highest bus number
273         * instead of the count.
274         */
275                n_bus++;
276#endif
277
278                unit = 0;
279                for ( i=0; (dr=libbsdport_netdriver_table[i]); i++ ) {
280
281                        /* unused slot ? */
282                        if ( 0 == dr->name && 0 == dr->methods )
283                                continue;
284
285                        /* Find matching driver */
286                        if ( libbsdportAttachVerbose )
287                                printf("Trying driver '%s' ...", dr->name);
288
289                        if ( matches(dr, nm) ) {
290
291                                if ( libbsdportAttachVerbose )
292                                        printf("MATCH\n");
293
294                                assert( dr->methods );
295
296                                thisunit = 0;
297
298                                if ( DEV_TYPE_PCI != dr->type ) {
299                                        fprintf(stderr,"Non-PCI driver '%s' not supported; skipping\n", dr->name);
300                                        continue;
301                                }
302
303                                dev = devalloc(dr);
304                                for ( b=0; b<n_bus; b++ )
305                                        for ( d=0; d<PCI_MAX_DEVICES; d++ ) {
306                                                for ( f=0; f<pci_num_functions(b,d); f++ ) {
307                                                        if ( ! pci_is_ether(b,d,f) )
308                                                                continue;
309
310                                                        dev->bushdr.pci.bus = b;
311                                                        dev->bushdr.pci.dev = d;
312                                                        dev->bushdr.pci.fun = f;
313
314                                                        bdfunit = (b<<8) | PCI_DEVFN(d,f);
315
316                                                        if ( libbsdportAttachVerbose ) {
317                                                                printf("Probing PCI 0x%x:%x.%x\n",
318                                                                                bdfunit>>8, PCI_SLOT(bdfunit), PCI_FUNC(bdfunit));
319                                                        }
320
321                                                        /* has this device been attached already ? */
322                                                        if ( (tmpdev = devattached(dev)) ) {
323                                                                if ( dev->drv == tmpdev->drv )
324                                                                        thisunit++;
325                                                                unit++;
326                                                                if ( UNITMATCH(wantedunit, unit, bdfunit) ) {
327                                                                        fprintf(stderr,"Device '%s' has already been attached\n", device_get_nameunit(dev));
328                                                                        error = EBUSY;
329                                                                        goto bail;
330                                                                }
331                                                        } else {
332                                                                switch ( ( prob = dr->methods->probe(dev) ) ) {
333                                                                                /* LOW_PRIORITY currently unsupported; list preferred drivers first */
334                                                                        case BUS_PROBE_LOW_PRIORITY:
335                                                                        case BUS_PROBE_DEFAULT:
336                                                                                /* accepted */
337                                                                                thisunit++;
338                                                                                unit++;
339                                                                                /* wanted unit == 0 means next avail.
340                                                                                 * unit is acceptable.
341                                                                                 */
342                                                                                if ( libbsdportAttachVerbose )
343                                                                                        printf("->SUCCESS\n");
344
345                                                                                if ( 0 == wantedunit || UNITMATCH(wantedunit, unit, bdfunit) ) {
346                                                                                                error = devattach(dev, thisunit, cfg);
347                                                                                                if ( !error )
348                                                                                                        dev = 0; /* is now on list */
349                                                                                                goto bail;
350                                                                                }
351                                                                                break;
352
353                                                                        default:
354                                                                                if ( libbsdportAttachVerbose )
355                                                                                        printf("->FAILED\n");
356                                                                                /* probe failed */
357                                                                                break;
358                                                                }
359                                                        }
360                                                        devclean(dev);
361                                                } /* for all functions */
362                                        } /* for all busses + slots */
363                                devfree(dev); dev = 0;
364                        } /* matching driver */
365                        else
366                        {
367                                if ( libbsdportAttachVerbose )
368                                        printf("NO MATCH\n");
369                        }
370                } /* for all drivers */
371
372                /* Nothing found */
373                error = ENODEV;
374bail:
375        if (dev)
376                devfree(dev);
377        return error;
378}
379
380device_t
381libbsdport_netdriver_get_dev(const char *name)
382{
383struct device *ldev;
384
385        if ( !name )
386                return 0;
387
388        rtems_bsdnet_semaphore_obtain();
389                STAILQ_FOREACH(ldev, &devq, list) {
390                        if ( !strcmp(name, device_get_nameunit(ldev)) )
391                                break;
392                }
393        rtems_bsdnet_semaphore_release();
394        return ldev;
395}
Note: See TracBrowser for help on using the repository browser.