if_sn_pccard.c revision 147797
1/*-
2 * Copyright (c) 1999 M. Warner Losh <imp@village.org>
3 * 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 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
18 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25/*
26 * Modifications for Megahertz X-Jack Ethernet Card (XJ-10BT)
27 *
28 * Copyright (c) 1996 by Tatsumi Hosokawa <hosokawa@jp.FreeBSD.org>
29 *                       BSD-nomads, Tokyo, Japan.
30 */
31
32#include <sys/cdefs.h>
33__FBSDID("$FreeBSD: head/sys/dev/sn/if_sn_pccard.c 147797 2005-07-06 15:59:47Z imp $");
34
35#include <sys/param.h>
36#include <sys/bus.h>
37#include <sys/kernel.h>
38#include <sys/module.h>
39#include <sys/socket.h>
40#include <sys/systm.h>
41
42#include <net/ethernet.h>
43#include <net/if.h>
44#include <net/if_arp.h>
45
46#include <machine/bus.h>
47
48#include <dev/pccard/pccardvar.h>
49#include <dev/pccard/pccard_cis.h>
50#include <dev/sn/if_snreg.h>
51#include <dev/sn/if_snvar.h>
52
53#include "card_if.h"
54#include "pccarddevs.h"
55
56static const struct pccard_product sn_pccard_products[] = {
57	PCMCIA_CARD(DSPSI, XJACK),
58	PCMCIA_CARD(NEWMEDIA, BASICS),
59	PCMCIA_CARD(SMC, SMC91C96),
60#if 0
61	PCMCIA_CARD(SMC, 8020BT),
62#endif
63	{ NULL }
64};
65
66static int
67sn_pccard_probe(device_t dev)
68{
69	const struct pccard_product *pp;
70	int		error;
71	uint32_t	fcn = PCCARD_FUNCTION_UNSPEC;
72
73	/* Make sure we're a network function */
74	error = pccard_get_function(dev, &fcn);
75	if (error != 0)
76		return (error);
77	if (fcn != PCCARD_FUNCTION_NETWORK)
78		return (ENXIO);
79
80	if ((pp = pccard_product_lookup(dev, sn_pccard_products,
81	    sizeof(sn_pccard_products[0]), NULL)) != NULL) {
82		if (pp->pp_name != NULL)
83			device_set_desc(dev, pp->pp_name);
84		return 0;
85	}
86	return EIO;
87}
88
89static int
90sn_pccard_ascii_enaddr(const char *str, u_char *enet)
91{
92        uint8_t digit;
93	int i;
94
95	memset(enet, 0, ETHER_ADDR_LEN);
96
97	for (i = 0, digit = 0; i < (ETHER_ADDR_LEN * 2); i++) {
98		if (str[i] >= '0' && str[i] <= '9')
99			digit |= str[i] - '0';
100		else if (str[i] >= 'a' && str[i] <= 'f')
101			digit |= (str[i] - 'a') + 10;
102		else if (str[i] >= 'A' && str[i] <= 'F')
103			digit |= (str[i] - 'A') + 10;
104		else {
105			/* Bogus digit!! */
106			return (0);
107		}
108
109		/* Compensate for ordering of digits. */
110		if (i & 1) {
111			enet[i >> 1] = digit;
112			digit = 0;
113		} else
114			digit <<= 4;
115	}
116
117	return (1);
118}
119
120static int
121sn_pccard_attach(device_t dev)
122{
123	struct sn_softc *sc = device_get_softc(dev);
124	const char *cisstr;
125	u_char eaddr[ETHER_ADDR_LEN];
126	int i, err;
127	uint16_t w;
128	u_char sum;
129
130	pccard_get_ether(dev, eaddr);
131	for (i = 0, sum = 0; i < ETHER_ADDR_LEN; i++)
132		sum |= eaddr[i];
133	if (sum == 0) {
134		pccard_get_cis3_str(dev, &cisstr);
135		if (cisstr && strlen(cisstr) == ETHER_ADDR_LEN * 2)
136		    sum = sn_pccard_ascii_enaddr(cisstr, eaddr);
137	}
138	if (sum == 0) {
139		pccard_get_cis4_str(dev, &cisstr);
140		if (cisstr && strlen(cisstr) == ETHER_ADDR_LEN * 2)
141		    sum = sn_pccard_ascii_enaddr(cisstr, eaddr);
142	}
143
144	/* Allocate resources so we can program the ether addr */
145	sc->dev = dev;
146	err = sn_activate(dev);
147	if (err) {
148		sn_deactivate(dev);
149		return (err);
150	}
151
152	if (sum) {
153		SMC_SELECT_BANK(sc, 1);
154		for (i = 0; i < 3; i++) {
155			w = (uint16_t)eaddr[i * 2] |
156			    (((uint16_t)eaddr[i * 2 + 1]) << 8);
157			CSR_WRITE_2(sc, IAR_ADDR0_REG_W + i * 2, w);
158		}
159	}
160	err = sn_attach(dev);
161	if (err)
162		sn_deactivate(dev);
163	return (err);
164}
165
166static device_method_t sn_pccard_methods[] = {
167	/* Device interface */
168	DEVMETHOD(device_probe,		sn_pccard_probe),
169	DEVMETHOD(device_attach,	sn_pccard_attach),
170	DEVMETHOD(device_detach,	sn_detach),
171
172	{ 0, 0 }
173};
174
175static driver_t sn_pccard_driver = {
176	"sn",
177	sn_pccard_methods,
178	sizeof(struct sn_softc),
179};
180
181extern devclass_t sn_devclass;
182
183DRIVER_MODULE(sn, pccard, sn_pccard_driver, sn_devclass, 0, 0);
184MODULE_DEPEND(sn, ether, 1, 1, 1);
185