cardbus_device.c revision 184981
1/*- 2 * Copyright (c) 2005, M. Warner Losh 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 unmodified, this list of conditions, and the following 10 * disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 * SUCH DAMAGE. 26 * 27 */ 28#include <sys/cdefs.h> 29__FBSDID("$FreeBSD: head/sys/dev/cardbus/cardbus_device.c 184981 2008-11-15 05:22:06Z imp $"); 30 31#include <sys/param.h> 32#include <sys/conf.h> 33#include <sys/malloc.h> 34#include <sys/systm.h> 35#include <sys/uio.h> 36 37#include <sys/bus.h> 38#include <machine/bus.h> 39#include <sys/rman.h> 40#include <machine/resource.h> 41 42#include <sys/pciio.h> 43#include <dev/pci/pcivar.h> 44#include <dev/pci/pcireg.h> 45#include <dev/pci/pci_private.h> 46 47#include <dev/cardbus/cardbusreg.h> 48#include <dev/cardbus/cardbusvar.h> 49#include <dev/cardbus/cardbus_cis.h> 50#include <dev/pccard/pccard_cis.h> 51 52static d_open_t cardbus_open; 53static d_close_t cardbus_close; 54static d_read_t cardbus_read; 55static d_ioctl_t cardbus_ioctl; 56 57static struct cdevsw cardbus_cdevsw = { 58 .d_version = D_VERSION, 59 .d_open = cardbus_open, 60 .d_close = cardbus_close, 61 .d_read = cardbus_read, 62 .d_ioctl = cardbus_ioctl, 63 .d_name = "cardbus" 64}; 65 66int 67cardbus_device_create(struct cardbus_softc *sc) 68{ 69 uint32_t minor; 70 71 minor = device_get_unit(sc->sc_dev) << 16; 72 sc->sc_cisdev = make_dev(&cardbus_cdevsw, minor, 0, 0, 0666, 73 "cardbus%u.cis", device_get_unit(sc->sc_dev)); 74 sc->sc_cisdev->si_drv1 = sc; 75 return (0); 76} 77 78int 79cardbus_device_destroy(struct cardbus_softc *sc) 80{ 81 if (sc->sc_cisdev) 82 destroy_dev(sc->sc_cisdev); 83 return (0); 84} 85 86static int 87cardbus_build_cis(device_t cbdev, device_t child, int id, 88 int len, uint8_t *tupledata, uint32_t start, uint32_t *off, 89 struct tuple_callbacks *info, void *argp) 90{ 91 struct cis_buffer *cis; 92 int i; 93 94 cis = (struct cis_buffer *)argp; 95 /* 96 * CISTPL_END is a special case, it has no length field. 97 */ 98 if (id == CISTPL_END) { 99 if (cis->len + 1 > sizeof(cis->buffer)) { 100 cis->len = 0; 101 return (ENOSPC); 102 } 103 cis->buffer[cis->len++] = id; 104 return (0); 105 } 106 if (cis->len + 2 + len > sizeof(cis->buffer)) { 107 cis->len = 0; 108 return (ENOSPC); 109 } 110 cis->buffer[cis->len++] = id; 111 cis->buffer[cis->len++] = len; 112 for (i = 0; i < len; i++) 113 cis->buffer[cis->len++] = tupledata[i]; 114 return (0); 115} 116 117static int 118cardbus_device_buffer_cis(device_t parent, device_t child) 119{ 120 struct cardbus_softc *sc; 121 struct tuple_callbacks cb[] = { 122 {CISTPL_GENERIC, "GENERIC", cardbus_build_cis} 123 }; 124 125 sc = device_get_softc(parent); 126 return (cardbus_parse_cis(parent, child, cb, &sc->sc_cis)); 127} 128 129static int 130cardbus_open(struct cdev *dev, int oflags, int devtype, struct thread *td) 131{ 132 device_t parent, child; 133 device_t *kids; 134 int cnt, err; 135 struct cardbus_softc *sc; 136 137 sc = dev->si_drv1; 138 if (sc->sc_cis_open) 139 return (EBUSY); 140 parent = sc->sc_dev; 141 err = device_get_children(parent, &kids, &cnt); 142 if (err) 143 return err; 144 sc->sc_cis.len = 0; 145 if (cnt == 0) { 146 free(kids, M_TEMP); 147 sc->sc_cis_open++; 148 return (0); 149 } 150 child = kids[0]; 151 free(kids, M_TEMP); 152 err = cardbus_device_buffer_cis(parent, child); 153 if (err) 154 return (err); 155 sc->sc_cis_open++; 156 return (0); 157} 158 159static int 160cardbus_close(struct cdev *dev, int fflags, int devtype, struct thread *td) 161{ 162 struct cardbus_softc *sc; 163 164 sc = dev->si_drv1; 165 sc->sc_cis_open = 0; 166 return (0); 167} 168 169static int 170cardbus_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag, 171 struct thread *td) 172{ 173 return (ENOTTY); 174} 175 176static int 177cardbus_read(struct cdev *dev, struct uio *uio, int ioflag) 178{ 179 struct cardbus_softc *sc; 180 181 sc = dev->si_drv1; 182 /* EOF */ 183 if (uio->uio_offset >= sc->sc_cis.len) 184 return (0); 185 return (uiomove(sc->sc_cis.buffer + uio->uio_offset, 186 MIN(uio->uio_resid, sc->sc_cis.len - uio->uio_offset), uio)); 187} 188