xref: /pciutils/lib/caps.c (revision ef78f397)
16d5e39acSMartin Mares /*
26d5e39acSMartin Mares  *	The PCI Library -- Capabilities
36d5e39acSMartin Mares  *
46d5e39acSMartin Mares  *	Copyright (c) 2008 Martin Mares <[email protected]>
56d5e39acSMartin Mares  *
661829219SMartin Mares  *	Can be freely distributed and used under the terms of the GNU GPL v2+.
761829219SMartin Mares  *
861829219SMartin Mares  *	SPDX-License-Identifier: GPL-2.0-or-later
96d5e39acSMartin Mares  */
106d5e39acSMartin Mares 
116d5e39acSMartin Mares #include <string.h>
126d5e39acSMartin Mares 
136d5e39acSMartin Mares #include "internal.h"
146d5e39acSMartin Mares 
156d5e39acSMartin Mares static void
pci_add_cap(struct pci_dev * d,unsigned int addr,unsigned int id,unsigned int type)166d5e39acSMartin Mares pci_add_cap(struct pci_dev *d, unsigned int addr, unsigned int id, unsigned int type)
176d5e39acSMartin Mares {
186d5e39acSMartin Mares   struct pci_cap *cap = pci_malloc(d->access, sizeof(*cap));
196d5e39acSMartin Mares 
209c48ed23SMartin Mares   if (d->last_cap)
219c48ed23SMartin Mares     d->last_cap->next = cap;
229c48ed23SMartin Mares   else
236d5e39acSMartin Mares     d->first_cap = cap;
249c48ed23SMartin Mares   d->last_cap = cap;
25ae94d494SMartin Mares   cap->next = NULL;
266d5e39acSMartin Mares   cap->addr = addr;
276d5e39acSMartin Mares   cap->id = id;
286d5e39acSMartin Mares   cap->type = type;
296d5e39acSMartin Mares   d->access->debug("%04x:%02x:%02x.%d: Found capability %04x of type %d at %04x\n",
306d5e39acSMartin Mares     d->domain, d->bus, d->dev, d->func, id, type, addr);
316d5e39acSMartin Mares }
326d5e39acSMartin Mares 
336d5e39acSMartin Mares static void
pci_scan_trad_caps(struct pci_dev * d)346d5e39acSMartin Mares pci_scan_trad_caps(struct pci_dev *d)
356d5e39acSMartin Mares {
366d5e39acSMartin Mares   word status = pci_read_word(d, PCI_STATUS);
376d5e39acSMartin Mares   byte been_there[256];
386d5e39acSMartin Mares   int where;
396d5e39acSMartin Mares 
406d5e39acSMartin Mares   if (!(status & PCI_STATUS_CAP_LIST))
416d5e39acSMartin Mares     return;
426d5e39acSMartin Mares 
436d5e39acSMartin Mares   memset(been_there, 0, 256);
446d5e39acSMartin Mares   where = pci_read_byte(d, PCI_CAPABILITY_LIST) & ~3;
456d5e39acSMartin Mares   while (where)
466d5e39acSMartin Mares     {
476d5e39acSMartin Mares       byte id = pci_read_byte(d, where + PCI_CAP_LIST_ID);
486d5e39acSMartin Mares       byte next = pci_read_byte(d, where + PCI_CAP_LIST_NEXT) & ~3;
496d5e39acSMartin Mares       if (been_there[where]++)
506d5e39acSMartin Mares 	break;
516d5e39acSMartin Mares       if (id == 0xff)
526d5e39acSMartin Mares 	break;
536d5e39acSMartin Mares       pci_add_cap(d, where, id, PCI_CAP_NORMAL);
546d5e39acSMartin Mares       where = next;
556d5e39acSMartin Mares     }
566d5e39acSMartin Mares }
576d5e39acSMartin Mares 
586d5e39acSMartin Mares static void
pci_scan_ext_caps(struct pci_dev * d)596d5e39acSMartin Mares pci_scan_ext_caps(struct pci_dev *d)
606d5e39acSMartin Mares {
616d5e39acSMartin Mares   byte been_there[0x1000];
626d5e39acSMartin Mares   int where = 0x100;
636d5e39acSMartin Mares 
646d5e39acSMartin Mares   if (!pci_find_cap(d, PCI_CAP_ID_EXP, PCI_CAP_NORMAL))
656d5e39acSMartin Mares     return;
666d5e39acSMartin Mares 
676d5e39acSMartin Mares   memset(been_there, 0, 0x1000);
686d5e39acSMartin Mares   do
696d5e39acSMartin Mares     {
706d5e39acSMartin Mares       u32 header;
716d5e39acSMartin Mares       int id;
726d5e39acSMartin Mares 
736d5e39acSMartin Mares       header = pci_read_long(d, where);
746d5e39acSMartin Mares       if (!header || header == 0xffffffff)
756d5e39acSMartin Mares 	break;
766d5e39acSMartin Mares       id = header & 0xffff;
776d5e39acSMartin Mares       if (been_there[where]++)
786d5e39acSMartin Mares 	break;
796d5e39acSMartin Mares       pci_add_cap(d, where, id, PCI_CAP_EXTENDED);
80d61c4772SMartin Mares       where = (header >> 20) & ~3;
816d5e39acSMartin Mares     }
826d5e39acSMartin Mares   while (where);
836d5e39acSMartin Mares }
846d5e39acSMartin Mares 
858e9299e4SMartin Mares void
pci_scan_caps(struct pci_dev * d,unsigned int want_fields)866d5e39acSMartin Mares pci_scan_caps(struct pci_dev *d, unsigned int want_fields)
876d5e39acSMartin Mares {
888e9299e4SMartin Mares   if (want_fields & PCI_FILL_EXT_CAPS)
896d5e39acSMartin Mares     want_fields |= PCI_FILL_CAPS;
906d5e39acSMartin Mares 
918e9299e4SMartin Mares   if (want_fill(d, want_fields, PCI_FILL_CAPS))
926d5e39acSMartin Mares     pci_scan_trad_caps(d);
938e9299e4SMartin Mares   if (want_fill(d, want_fields, PCI_FILL_EXT_CAPS))
946d5e39acSMartin Mares     pci_scan_ext_caps(d);
956d5e39acSMartin Mares }
966d5e39acSMartin Mares 
976d5e39acSMartin Mares void
pci_free_caps(struct pci_dev * d)986d5e39acSMartin Mares pci_free_caps(struct pci_dev *d)
996d5e39acSMartin Mares {
1006d5e39acSMartin Mares   struct pci_cap *cap;
1016d5e39acSMartin Mares 
1026d5e39acSMartin Mares   while (cap = d->first_cap)
1036d5e39acSMartin Mares     {
104adb9abcfSMartin Mares       d->first_cap = cap->next;
105adb9abcfSMartin Mares       pci_mfree(cap);
1066d5e39acSMartin Mares     }
1076d5e39acSMartin Mares }
1086d5e39acSMartin Mares 
1096d5e39acSMartin Mares struct pci_cap *
pci_find_cap(struct pci_dev * d,unsigned int id,unsigned int type)1106d5e39acSMartin Mares pci_find_cap(struct pci_dev *d, unsigned int id, unsigned int type)
1116d5e39acSMartin Mares {
112c22c314aSDaniel Schaefer   return pci_find_cap_nr(d, id, type, NULL);
113c22c314aSDaniel Schaefer }
1146d5e39acSMartin Mares 
115c22c314aSDaniel Schaefer /**
116c22c314aSDaniel Schaefer  * Finds a particular capability of a device
117c22c314aSDaniel Schaefer  *
118d7d9e305SMartin Mares  * To select one capability if there are more than one with the same id, you
119c22c314aSDaniel Schaefer  * can provide a pointer to an unsigned int that contains the index which you
120c22c314aSDaniel Schaefer  * want as cap_number. If you don't care and are fine with the first one you
1218f33a693SGuillem Jover  * can supply NULL. The cap_number will be replaced by the actual number
1228f33a693SGuillem Jover  * of capabilities with that id.
123c22c314aSDaniel Schaefer  */
124c22c314aSDaniel Schaefer struct pci_cap *
pci_find_cap_nr(struct pci_dev * d,unsigned int id,unsigned int type,unsigned int * cap_number)125c22c314aSDaniel Schaefer pci_find_cap_nr(struct pci_dev *d, unsigned int id, unsigned int type,
126c22c314aSDaniel Schaefer                 unsigned int *cap_number)
127c22c314aSDaniel Schaefer {
128c22c314aSDaniel Schaefer   struct pci_cap *c;
129d7d9e305SMartin Mares   struct pci_cap *found = NULL;
130d7d9e305SMartin Mares   unsigned int target = (cap_number ? *cap_number : 0);
131d7d9e305SMartin Mares   unsigned int index = 0;
132d7d9e305SMartin Mares 
133*ef78f397SMartin Mares   pci_fill_info_v313(d, ((type == PCI_CAP_NORMAL) ? PCI_FILL_CAPS : PCI_FILL_EXT_CAPS));
134d7d9e305SMartin Mares 
135d7d9e305SMartin Mares   for (c=d->first_cap; c; c=c->next)
136c22c314aSDaniel Schaefer     {
137d7d9e305SMartin Mares       if (c->type == type && c->id == id)
138d7d9e305SMartin Mares 	{
139d7d9e305SMartin Mares 	  if (target == index)
140d7d9e305SMartin Mares 	    found = c;
141d7d9e305SMartin Mares 	  index++;
142c22c314aSDaniel Schaefer 	}
143bc68c6cfSMartin Mares     }
144c22c314aSDaniel Schaefer 
145d7d9e305SMartin Mares   if (cap_number)
146d7d9e305SMartin Mares     *cap_number = index;
147d7d9e305SMartin Mares   return found;
1486d5e39acSMartin Mares }
149