Initial import of binutils 2.22 on the new vendor branch
[dragonfly.git] / sys / dev / netif / ndis / if_ndis_pci.c
1 /*-
2  * Copyright (c) 2003
3  *      Bill Paul <wpaul@windriver.com>.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgement:
15  *      This product includes software developed by Bill Paul.
16  * 4. Neither the name of the author nor the names of any co-contributors
17  *    may be used to endorse or promote products derived from this software
18  *    without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD
24  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
30  * THE POSSIBILITY OF SUCH DAMAGE.
31  *
32  * $FreeBSD: src/sys/dev/if_ndis/if_ndis_pci.c,v 1.26 2010/12/19 11:14:34 tijl Exp $
33  */
34
35 #include <sys/param.h>
36 #include <sys/systm.h>
37 #include <sys/kernel.h>
38 #include <sys/module.h>
39 #include <sys/socket.h>
40 #include <sys/queue.h>
41 #include <sys/sysctl.h>
42 #include <sys/lock.h>
43
44 #include <net/if.h>
45 #include <net/if_arp.h>
46 #include <net/if_media.h>
47
48 #include <sys/bus.h>
49 #include <sys/rman.h>
50
51 #include <netproto/802_11/ieee80211_var.h>
52
53 #include <bus/pci/pcireg.h>
54 #include <bus/pci/pcivar.h>
55 #include <bus/usb/usb.h>
56 #include <bus/usb/usbdi.h>
57
58 #include <emulation/ndis/pe_var.h>
59 #include <emulation/ndis/cfg_var.h>
60 #include <emulation/ndis/resource_var.h>
61 #include <emulation/ndis/ntoskrnl_var.h>
62 #include <emulation/ndis/ndis_var.h>
63 #include <dev/netif/ndis/if_ndisvar.h>
64
65 MODULE_DEPEND(if_ndis, pci, 1, 1, 1);
66
67 static int ndis_probe_pci       (device_t);
68 static int ndis_attach_pci      (device_t);
69 static int ndis_detach_pci      (device_t);
70 static struct resource_list *ndis_get_resource_list
71                                 (device_t, device_t);
72 static int ndis_devcompare      (interface_type,
73                                  struct ndis_pci_type *, device_t);
74 extern int ndisdrv_modevent     (module_t, int, void *);
75 extern int ndis_attach          (device_t);
76 extern int ndis_shutdown        (device_t);
77 extern int ndis_detach          (device_t);
78 extern int ndis_suspend         (device_t);
79 extern int ndis_resume          (device_t);
80
81 static device_method_t ndis_methods[] = {
82         /* Device interface */
83         DEVMETHOD(device_probe,         ndis_probe_pci),
84         DEVMETHOD(device_attach,        ndis_attach_pci),
85         DEVMETHOD(device_detach,        ndis_detach_pci),
86         DEVMETHOD(device_shutdown,      ndis_shutdown),
87         DEVMETHOD(device_suspend,       ndis_suspend),
88         DEVMETHOD(device_resume,        ndis_resume),
89
90         /* Bus interface */
91         DEVMETHOD(bus_get_resource_list, ndis_get_resource_list),
92
93         { 0, 0 }
94 };
95
96 static driver_t ndis_driver = {
97         "ndis",
98         ndis_methods,
99         sizeof(struct ndis_softc)
100 };
101
102 static devclass_t ndis_devclass;
103
104 DRIVER_MODULE(if_ndis, pci, ndis_driver, ndis_devclass, ndisdrv_modevent, NULL);
105 DRIVER_MODULE(if_ndis, cardbus, ndis_driver, ndis_devclass, ndisdrv_modevent, NULL);
106
107 static int
108 ndis_devcompare(interface_type bustype, struct ndis_pci_type *t, device_t dev)
109 {
110         uint16_t                vid, did;
111         uint32_t                subsys;
112
113         if (bustype != PCIBus)
114                 return(FALSE);
115
116         vid = pci_get_vendor(dev);
117         did = pci_get_device(dev);
118         subsys = pci_get_subdevice(dev);
119         subsys = (subsys << 16) | pci_get_subvendor(dev);
120
121         while(t->ndis_name != NULL) {
122                 if ((t->ndis_vid == vid) && (t->ndis_did == did) &&
123                     (t->ndis_subsys == subsys || t->ndis_subsys == 0)) {
124                         device_set_desc(dev, t->ndis_name);
125                         return(TRUE);
126                 }
127                 t++;
128         }
129
130         return(FALSE);
131 }
132
133 /*
134  * Probe for an NDIS device. Check the PCI vendor and device
135  * IDs against our list and return a device name if we find a match.
136  */
137 static int
138 ndis_probe_pci(device_t dev)
139 {
140         driver_object           *drv;
141         struct drvdb_ent        *db;
142
143         drv = windrv_lookup(0, "PCI Bus");
144
145         if (drv == NULL)
146                 return(ENXIO);
147
148         db = windrv_match((matchfuncptr)ndis_devcompare, dev);
149
150         if (db != NULL) {
151                 /* Create PDO for this device instance */
152                 windrv_create_pdo(drv, dev);
153                 return(0);
154         }
155
156         return(ENXIO);
157 }
158
159 /*
160  * Attach the interface. Allocate softc structures, do ifmedia
161  * setup and ethernet/BPF attach.
162  */
163 static int
164 ndis_attach_pci(device_t dev)
165 {
166         struct ndis_softc       *sc;
167         int                     unit, error = 0, rid;
168         struct ndis_pci_type    *t;
169         int                     devidx = 0, defidx = 0;
170         struct resource_list    *rl;
171         struct resource_list_entry      *rle;
172         struct drvdb_ent        *db;
173         uint16_t                vid, did;
174         uint32_t                subsys;
175
176         wlan_serialize_enter();
177
178         sc = device_get_softc(dev);
179         unit = device_get_unit(dev);
180         sc->ndis_dev = dev;
181
182         db = windrv_match((matchfuncptr)ndis_devcompare, dev);
183         if (db == NULL) {
184                 wlan_serialize_exit();
185                 return (ENXIO);
186         }
187         sc->ndis_dobj = db->windrv_object;
188         sc->ndis_regvals = db->windrv_regvals;
189
190         /*
191          * Map control/status registers.
192          */
193
194         pci_enable_busmaster(dev);
195
196         rl = BUS_GET_RESOURCE_LIST(device_get_parent(dev), dev);
197         if (rl != NULL) {
198                 SLIST_FOREACH(rle, rl, link) {
199                         switch (rle->type) {
200                         case SYS_RES_IOPORT:
201                                 sc->ndis_io_rid = rle->rid;
202                                 sc->ndis_res_io = bus_alloc_resource_any(dev,
203                                     SYS_RES_IOPORT, &sc->ndis_io_rid,
204                                     RF_ACTIVE);
205                                 if (sc->ndis_res_io == NULL) {
206                                         device_printf(dev,
207                                             "couldn't map iospace\n");
208                                         error = ENXIO;
209                                         goto fail;
210                                 }
211                                 break;
212                         case SYS_RES_MEMORY:
213                                 if (sc->ndis_res_altmem != NULL &&
214                                     sc->ndis_res_mem != NULL) {
215                                         device_printf(dev,
216                                             "too many memory resources\n");
217                                         error = ENXIO;
218                                         goto fail;
219                                 }
220                                 if (sc->ndis_res_mem) {
221                                         sc->ndis_altmem_rid = rle->rid;
222                                         sc->ndis_res_altmem =
223                                             bus_alloc_resource_any(dev,
224                                                 SYS_RES_MEMORY,
225                                                 &sc->ndis_altmem_rid,
226                                                 RF_ACTIVE);
227                                         if (sc->ndis_res_altmem == NULL) {
228                                                 device_printf(dev,
229                                                     "couldn't map alt "
230                                                     "memory\n");
231                                                 error = ENXIO;
232                                                 goto fail;
233                                         }
234                                 } else {
235                                         sc->ndis_mem_rid = rle->rid;
236                                         sc->ndis_res_mem =
237                                             bus_alloc_resource_any(dev,
238                                                 SYS_RES_MEMORY,
239                                                 &sc->ndis_mem_rid,
240                                                 RF_ACTIVE);
241                                         if (sc->ndis_res_mem == NULL) {
242                                                 device_printf(dev,
243                                                     "couldn't map memory\n");
244                                                 error = ENXIO;
245                                                 goto fail;
246                                         }
247                                 }
248                                 break;
249                         case SYS_RES_IRQ:
250                                 rid = rle->rid;
251                                 sc->ndis_irq = bus_alloc_resource_any(dev,
252                                     SYS_RES_IRQ, &rid,
253                                     RF_SHAREABLE | RF_ACTIVE);
254                                 if (sc->ndis_irq == NULL) {
255                                         device_printf(dev,
256                                             "couldn't map interrupt\n");
257                                         error = ENXIO;
258                                         goto fail;
259                                 }
260                                 break;
261                         default:
262                                 break;
263                         }
264                         sc->ndis_rescnt++;
265                 }
266         }
267
268         /*
269          * If the BIOS did not set up an interrupt for this device,
270          * the resource traversal code above will fail to set up
271          * an IRQ resource. This is usually a bad thing, so try to
272          * force the allocation of an interrupt here. If one was
273          * not assigned to us by the BIOS, bus_alloc_resource()
274          * should route one for us.
275          */
276         if (sc->ndis_irq == NULL) {
277                 rid = 0;
278                 sc->ndis_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ,
279                     &rid, RF_SHAREABLE | RF_ACTIVE);
280                 if (sc->ndis_irq == NULL) {
281                         device_printf(dev, "couldn't route interrupt\n");
282                         error = ENXIO;
283                         goto fail;
284                 }
285                 sc->ndis_rescnt++;
286         }
287
288         /*
289          * Allocate the parent bus DMA tag appropriate for PCI.
290          */
291 #define NDIS_NSEG_NEW 32
292         error = bus_dma_tag_create(NULL,        /* parent */
293                         1, 0,                   /* alignment, boundary */
294                         BUS_SPACE_MAXADDR_32BIT,/* lowaddr */
295                         BUS_SPACE_MAXADDR,      /* highaddr */
296                         NULL, NULL,             /* filter, filterarg */
297                         MAXBSIZE, NDIS_NSEG_NEW,/* maxsize, nsegments */
298                         BUS_SPACE_MAXSIZE_32BIT,/* maxsegsize */
299                         BUS_DMA_ALLOCNOW,       /* flags */
300                         &sc->ndis_parent_tag);
301
302         if (error)
303                 goto fail;
304
305         sc->ndis_iftype = PCIBus;
306
307         /* Figure out exactly which device we matched. */
308
309         vid = pci_get_vendor(dev);
310         did = pci_get_device(dev);
311         subsys = pci_get_subdevice(dev);
312         subsys = (subsys << 16) | pci_get_subvendor(dev);
313
314         t = db->windrv_devlist;
315
316         while(t->ndis_name != NULL) {
317                 if (t->ndis_vid == vid && t->ndis_did == did) {
318                         if (t->ndis_subsys == 0)
319                                 defidx = devidx;
320                         else if (t->ndis_subsys == subsys)
321                                 break;
322                 }
323                 t++;
324                 devidx++;
325         }
326
327         if (t->ndis_name == NULL)
328                 sc->ndis_devidx = defidx;
329         else
330                 sc->ndis_devidx = devidx;
331
332         error = ndis_attach(dev);
333
334 fail:
335         wlan_serialize_exit();
336         return(error);
337 }
338
339 static int
340 ndis_detach_pci(device_t dev)
341 {
342         int error = 0;
343
344         wlan_serialize_enter();
345         error = ndis_detach(dev);
346         wlan_serialize_exit();
347         return(error);
348 }
349
350 static struct resource_list *
351 ndis_get_resource_list(device_t dev, device_t child)
352 {
353         struct ndis_softc       *sc;
354
355         sc = device_get_softc(dev);
356         return (BUS_GET_RESOURCE_LIST(device_get_parent(sc->ndis_dev), dev));
357 }