1131313Snjl/*-
2131313Snjl * Copyright (c) 2004 Nate Lawson (SDG)
3131313Snjl * All rights reserved.
4131313Snjl *
5131313Snjl * Redistribution and use in source and binary forms, with or without
6131313Snjl * modification, are permitted provided that the following conditions
7131313Snjl * are met:
8131313Snjl * 1. Redistributions of source code must retain the above copyright
9131313Snjl *    notice, this list of conditions and the following disclaimer.
10131313Snjl * 2. Redistributions in binary form must reproduce the above copyright
11131313Snjl *    notice, this list of conditions and the following disclaimer in the
12131313Snjl *    documentation and/or other materials provided with the distribution.
13131313Snjl *
14131313Snjl * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15131313Snjl * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16131313Snjl * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17131313Snjl * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18131313Snjl * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19131313Snjl * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20131313Snjl * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21131313Snjl * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22131313Snjl * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23131313Snjl * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24131313Snjl * SUCH DAMAGE.
25131313Snjl */
26131313Snjl
27148318Snjl#include <sys/cdefs.h>
28148318Snjl__FBSDID("$FreeBSD$");
29148318Snjl
30131313Snjl#include <sys/param.h>
31131313Snjl#include <sys/bus.h>
32131313Snjl
33193530Sjkim#include <contrib/dev/acpica/include/acpi.h>
34193530Sjkim
35131313Snjl#include <dev/acpica/acpivar.h>
36131313Snjl
37131313Snjlenum ops_t {
38131313Snjl	OP_NONE,
39131313Snjl	OP_LEQ,
40131313Snjl	OP_GEQ,
41131313Snjl	OP_EQL,
42131313Snjl};
43131313Snjl
44131313Snjlenum val_t {
45131313Snjl	OEM,
46131313Snjl	OEM_REV,
47131313Snjl	CREATOR,
48131313Snjl	CREATOR_REV,
49131313Snjl};
50131313Snjl
51131313Snjlstruct acpi_q_rule {
52167814Sjkim    char	sig[ACPI_NAME_SIZE];	/* Table signature to match */
53131313Snjl    enum val_t	val;
54131313Snjl    union {
55131313Snjl	char	*id;
56131313Snjl	enum ops_t op;
57131313Snjl    } x;
58131313Snjl    union {
59131313Snjl	char	*tid;
60131313Snjl	int	rev;
61131313Snjl    } y;
62131313Snjl};
63131313Snjl
64131313Snjlstruct acpi_q_entry {
65131313Snjl    const struct acpi_q_rule *match;
66131313Snjl    int		quirks;
67131313Snjl};
68131313Snjl
69131313Snjl#include "acpi_quirks.h"
70131313Snjl
71131313Snjlstatic int	aq_revcmp(int revision, enum ops_t op, int value);
72131313Snjlstatic int	aq_strcmp(char *actual, char *possible);
73131313Snjlstatic int	aq_match_header(ACPI_TABLE_HEADER *hdr,
74131313Snjl		    const struct acpi_q_rule *match);
75131313Snjl
76131313Snjlstatic int
77131313Snjlaq_revcmp(int revision, enum ops_t op, int value)
78131313Snjl{
79131313Snjl    switch (op) {
80131313Snjl    case OP_LEQ:
81131313Snjl	if (revision <= value)
82131313Snjl	    return (TRUE);
83131313Snjl	break;
84131313Snjl    case OP_GEQ:
85131313Snjl	if (revision >= value)
86131313Snjl	    return (TRUE);
87131313Snjl	break;
88131313Snjl    case OP_EQL:
89131313Snjl	if (revision == value)
90131313Snjl	    return (TRUE);
91131313Snjl	break;
92131313Snjl    case OP_NONE:
93131313Snjl	return (TRUE);
94131313Snjl    default:
95131313Snjl	panic("aq_revcmp: invalid op %d", op);
96131313Snjl    }
97131313Snjl
98131313Snjl    return (FALSE);
99131313Snjl}
100131313Snjl
101131313Snjlstatic int
102131313Snjlaq_strcmp(char *actual, char *possible)
103131313Snjl{
104131313Snjl    if (actual == NULL || possible == NULL)
105131313Snjl	return (TRUE);
106131313Snjl    return (strncmp(actual, possible, strlen(possible)) == 0);
107131313Snjl}
108131313Snjl
109131313Snjlstatic int
110131313Snjlaq_match_header(ACPI_TABLE_HEADER *hdr, const struct acpi_q_rule *match)
111131313Snjl{
112131313Snjl    int result;
113131313Snjl
114131313Snjl    result = FALSE;
115131313Snjl    switch (match->val) {
116131313Snjl    case OEM:
117131313Snjl	if (aq_strcmp(hdr->OemId, match->x.id) &&
118131313Snjl	    aq_strcmp(hdr->OemTableId, match->y.tid))
119131313Snjl	    result = TRUE;
120131313Snjl	break;
121131313Snjl    case CREATOR:
122131313Snjl	if (aq_strcmp(hdr->AslCompilerId, match->x.id))
123131313Snjl	    result = TRUE;
124131313Snjl	break;
125131313Snjl    case OEM_REV:
126131313Snjl	if (aq_revcmp(hdr->OemRevision, match->x.op, match->y.rev))
127131313Snjl	    result = TRUE;
128131313Snjl	break;
129131313Snjl    case CREATOR_REV:
130131313Snjl	if (aq_revcmp(hdr->AslCompilerRevision, match->x.op, match->y.rev))
131131313Snjl	    result = TRUE;
132131313Snjl	break;
133131313Snjl    }
134131313Snjl
135131313Snjl    return (result);
136131313Snjl}
137131313Snjl
138131313Snjlint
139131313Snjlacpi_table_quirks(int *quirks)
140131313Snjl{
141131313Snjl    const struct acpi_q_entry *entry;
142131313Snjl    const struct acpi_q_rule *match;
143167814Sjkim    ACPI_TABLE_HEADER fadt, dsdt, xsdt, *hdr;
144131313Snjl    int done;
145131313Snjl
146131313Snjl    /* First, allow the machdep system to set its idea of quirks. */
147131313Snjl    KASSERT(quirks != NULL, ("acpi quirks ptr is NULL"));
148131313Snjl    acpi_machdep_quirks(quirks);
149131313Snjl
150167814Sjkim    if (ACPI_FAILURE(AcpiGetTableHeader(ACPI_SIG_FADT, 0, &fadt)))
151167814Sjkim	bzero(&fadt, sizeof(fadt));
152167814Sjkim    if (ACPI_FAILURE(AcpiGetTableHeader(ACPI_SIG_DSDT, 0, &dsdt)))
153183190Sjkim	bzero(&dsdt, sizeof(dsdt));
154167814Sjkim    if (ACPI_FAILURE(AcpiGetTableHeader(ACPI_SIG_XSDT, 0, &xsdt)))
155183190Sjkim	bzero(&xsdt, sizeof(xsdt));
156167814Sjkim
157131313Snjl    /* Then, override the quirks with any matched from table signatures. */
158131313Snjl    for (entry = acpi_quirks_table; entry->match; entry++) {
159131313Snjl	done = TRUE;
160167814Sjkim	for (match = entry->match; match->sig[0] != '\0'; match++) {
161167814Sjkim	    if (!strncmp(match->sig, "FADT", ACPI_NAME_SIZE))
162167814Sjkim		hdr = &fadt;
163167814Sjkim	    else if (!strncmp(match->sig, ACPI_SIG_DSDT, ACPI_NAME_SIZE))
164167814Sjkim		hdr = &dsdt;
165167814Sjkim	    else if (!strncmp(match->sig, ACPI_SIG_XSDT, ACPI_NAME_SIZE))
166167814Sjkim		hdr = &xsdt;
167167814Sjkim	    else
168131313Snjl		panic("invalid quirk header\n");
169131313Snjl
170131313Snjl	    /* If we don't match any, skip to the next entry. */
171167814Sjkim	    if (aq_match_header(hdr, match) == FALSE) {
172131313Snjl		done = FALSE;
173131313Snjl		break;
174131313Snjl	    }
175131313Snjl	}
176131313Snjl
177131313Snjl	/* If all entries matched, update the quirks and return. */
178131313Snjl	if (done) {
179131313Snjl	    *quirks = entry->quirks;
180131313Snjl	    break;
181131313Snjl	}
182131313Snjl    }
183131313Snjl
184131313Snjl    return (0);
185131313Snjl}
186