Date: Mon, 03 Apr 2006 09:51:08 -0600 (MDT) From: "M. Warner Losh" <imp@bsdimp.com> To: jackie7691@yahoo.com.mx Cc: freebsd-drivers@freebsd.org Subject: Re: Driver for PCCARD / PCMCIA Message-ID: <20060403.095108.23089926.imp@bsdimp.com> In-Reply-To: <20060403144909.23793.qmail@web33101.mail.mud.yahoo.com> References: <20060403144909.23793.qmail@web33101.mail.mud.yahoo.com>
next in thread | previous in thread | raw e-mail | index | archive | help
In message: <20060403144909.23793.qmail@web33101.mail.mud.yahoo.com> Jacqueline P <jackie7691@yahoo.com.mx> writes: : I'm new to FreeBSD (5.3) and must develop (porting from Linux) a : driver for a PCCARD smartcardreader. But the documentation I found : was poor. :-( Unlike Linux, FreeBSD's PC Card driver integration is easy. In Linux, you have to worry about all of the card services-like routines and getting them into place. On FreeBSD, you just allocate your resources like you would for any other driver and you are basically done. : Could someone give me some hints about other documentation related : to this topic ? Is there an example driver ? The only examples I : came along were drivers for PCCARD - network cards. Actually, network drivers are quite instructive. Take the ex driver, for instance. It is a driver that supports a limited number of cards, so should be easy to cut and paste here. I've annotated it with C++ style comments. As you can see, it is pretty straight forward to create a front end. Warner /*- * Copyright (c) 2000 Mitsuru IWASAKI * All rights reserved. ... omitted, please see the source */ // The following includes are boilerplate for just about any driver // the FreeBSD ID is there if you ever want to integrate it into // FreeBSD. #include <sys/cdefs.h> __FBSDID("$FreeBSD: src/sys/dev/ex/if_ex_pccard.c,v 1.17 2005/09/13 19:28:03 imp Exp $"); #include <sys/param.h> #include <sys/systm.h> #include <sys/kernel.h> #include <sys/socket.h> #include <sys/module.h> #include <sys/bus.h> #include <machine/bus.h> #include <machine/resource.h> #include <sys/rman.h> // The following includes are network specific, you'll likely not need them. #include <net/if.h> #include <net/if_arp.h> #include <net/if_media.h> // The following includes are for the base part of the device. This // is the part of the device that's not dependent on its bus // attachment. Coming from Linux, there's a tendancy to comingle the // bus dependent stuff from the bus independent stuff. #include <dev/ex/if_exreg.h> #include <dev/ex/if_exvar.h> // The following 3 includes are to interact with the pccard system // headers. #include <dev/pccard/pccardvar.h> #include <dev/pccard/pccard_cis.h> // pccarddevs.h is generated from pccarddevs. It is a design pattern // that we use in FreeBSD. The card's manufacturer ID and product ID // are placed there. The following macros expand to the symbols // defined there, making the table easy to populate and read. #include "pccarddevs.h" static const struct pccard_product ex_pccard_products[] = { PCMCIA_CARD(OLICOM, OC2220), PCMCIA_CARD(OLICOM, OC2231), PCMCIA_CARD(OLICOM, OC2232), PCMCIA_CARD(INTEL, ETHEREXPPRO), { NULL } }; /* Bus Front End Functions */ static int ex_pccard_probe(device_t); static int ex_pccard_attach(device_t); // This function you'll likely not need, since it just performs a // sanity check on the MAC address. static int ex_pccard_enet_ok(u_char *enaddr) { int i; u_char sum; if (enaddr[0] == 0xff) return (0); for (i = 0, sum = 0; i < ETHER_ADDR_LEN; i++) sum |= enaddr[i]; return (sum != 0); } // Silicom products need special treatment, so ignore this static int ex_pccard_silicom_cb(const struct pccard_tuple *tuple, void *arg) { u_char *enaddr = arg; int i; if (tuple->code != CISTPL_FUNCE) return (0); if (tuple->length != 15) return (0); if (pccard_tuple_read_1(tuple, 6) != 6) return (0); for (i = 0; i < 6; i++) enaddr[i] = pccard_tuple_read_1(tuple, 7 + i); return (1); } // Silicom products need special treatment, so ignore this static void ex_pccard_get_silicom_mac(device_t dev, u_char *ether_addr) { pccard_cis_scan(dev, ex_pccard_silicom_cb, ether_addr); } // Probe the card. Make sure that it is a network device and that it // exists in the table. Chances are good you'll not want to have a // device type check in your dirver since the check is there for MFC // cards. If you have the right table, the following routine should // just be a few lines. static int ex_pccard_probe(device_t dev) { const struct pccard_product *pp; int error; uint32_t fcn = PCCARD_FUNCTION_UNSPEC; /* Make sure we're a network function */ error = pccard_get_function(dev, &fcn); if (error != 0) return (error); if (fcn != PCCARD_FUNCTION_NETWORK) return (ENXIO); if ((pp = pccard_product_lookup(dev, ex_pccard_products, sizeof(ex_pccard_products[0]), NULL)) != NULL) { if (pp->pp_name != NULL) device_set_desc(dev, pp->pp_name); return 0; } return EIO; } // Attach fills in the bus front end stuff. static int ex_pccard_attach(device_t dev) { struct ex_softc * sc = device_get_softc(dev); int error = 0; // won't need ethernet for smart card reader. u_char ether_addr[ETHER_ADDR_LEN]; sc->dev = dev; sc->ioport_rid = 0; sc->irq_rid = 0; // ex_alloc_resources is a routine in the base driver that // does bus_alloc_resource for all the necessary resources // for this card to operate. if ((error = ex_alloc_resources(dev)) != 0) { device_printf(dev, "ex_alloc_resources() failed!\n"); goto bad; } /* * Fill in several fields of the softc structure: * - Hardware Ethernet address. * - IRQ number. */ sc->irq_no = rman_get_start(sc->irq); // Won't need any of this stuff to find the MAC address for // this part. Ignore it for now. /* Try to get the ethernet address from the chip, then the CIS */ ex_get_address(sc, ether_addr); if (!ex_pccard_enet_ok(ether_addr)) pccard_get_ether(dev, ether_addr); if (!ex_pccard_enet_ok(ether_addr)) ex_pccard_get_silicom_mac(dev, ether_addr); if (!ex_pccard_enet_ok(ether_addr)) { device_printf(dev, "No NIC address found.\n"); error = ENXIO; goto bad; } bcopy(ether_addr, sc->enaddr, ETHER_ADDR_LEN); // ex_attach does the bus independent attaching of the code. if ((error = ex_attach(dev)) != 0) { device_printf(dev, "ex_attach() failed!\n"); goto bad; } // ex_attach doesn't setup an interrupt. Looking at the // different bus front ends, maybe it should be moved up into // ex_attach. It is likely this // is just a left-over from when we thought establishing // interrupt handlers in the bus dependent code was better // than in the bus independent code. Since these cards are // rare, no one has optimized it. error = bus_setup_intr(dev, sc->irq, INTR_TYPE_NET, ex_intr, (void *)sc, &sc->ih); if (error) { device_printf(dev, "bus_setup_intr() failed!\n"); goto bad; } // Success, return 0. return(0); bad: // Failure cleanup and return the error ex_release_resources(dev); return (error); } static device_method_t ex_pccard_methods[] = { /* Device interface */ DEVMETHOD(device_probe, ex_pccard_probe), DEVMETHOD(device_attach, ex_pccard_attach), DEVMETHOD(device_detach, ex_detach), { 0, 0 } }; static driver_t ex_pccard_driver = { "ex", ex_pccard_methods, sizeof(struct ex_softc), }; DRIVER_MODULE(ex, pccard, ex_pccard_driver, ex_devclass, 0, 0);
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20060403.095108.23089926.imp>