1 /* 2 * Copyright (c) 2009 The DragonFly Project. All rights reserved. 3 * 4 * This code is derived from software contributed to The DragonFly Project 5 * by Alex Hornung <ahornung@gmail.com> 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in 15 * the documentation and/or other materials provided with the 16 * distribution. 17 * 3. Neither the name of The DragonFly Project nor the names of its 18 * contributors may be used to endorse or promote products derived 19 * from this software without specific, prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 22 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 24 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 25 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 26 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, 27 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 28 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 29 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 30 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 31 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 * SUCH DAMAGE. 33 */ 34 35 #include <sys/param.h> 36 #include <sys/conf.h> 37 #include <sys/kernel.h> 38 #include <sys/systm.h> 39 #include <sys/limits.h> 40 #include <sys/malloc.h> 41 #include <sys/ctype.h> 42 #include <sys/sbuf.h> 43 #include <sys/queue.h> 44 #include <dev/misc/gpio/gpio.h> 45 #include <sys/uio.h> 46 #include <sys/lock.h> 47 #include <sys/devfs.h> 48 49 struct ledsc { 50 LIST_ENTRY(ledsc) list; 51 struct gpio *gp; 52 int pin; 53 cdev_t dev; 54 int unit; 55 int opened; 56 char *name; 57 struct gpio_mapping *gp_map; 58 }; 59 60 DEVFS_DEFINE_CLONE_BITMAP(nled); 61 static struct lock led_lock; 62 static LIST_HEAD(, ledsc) led_list = LIST_HEAD_INITIALIZER(&led_list); 63 static MALLOC_DEFINE(M_LED, "LED", "LED driver"); 64 65 66 static int 67 led_open(struct dev_open_args *ap) 68 { 69 struct ledsc *sc; 70 cdev_t dev; 71 72 dev = ap->a_head.a_dev; 73 sc = dev->si_drv1; 74 75 lockmgr(&led_lock, LK_EXCLUSIVE); 76 if (sc->opened) { 77 lockmgr(&led_lock, LK_RELEASE); 78 return EBUSY; 79 } 80 sc->opened = 1; 81 lockmgr(&led_lock, LK_RELEASE); 82 83 return 0; 84 } 85 86 static int 87 led_close(struct dev_close_args *ap) 88 { 89 struct ledsc *sc; 90 cdev_t dev; 91 92 dev = ap->a_head.a_dev; 93 sc = dev->si_drv1; 94 95 lockmgr(&led_lock, LK_EXCLUSIVE); 96 if (sc->opened) 97 sc->opened = 0; 98 lockmgr(&led_lock, LK_RELEASE); 99 100 return 0; 101 } 102 103 static int 104 led_write(struct dev_write_args *ap) 105 { 106 struct ledsc *sc; 107 cdev_t dev; 108 int error; 109 int data = 0; 110 int len; 111 112 dev = ap->a_head.a_dev; 113 sc = dev->si_drv1; 114 115 if (ap->a_uio->uio_resid > sizeof(int)) 116 return EINVAL; 117 118 len = ap->a_uio->uio_resid; 119 120 error = uiomove((void *)&data, ap->a_uio->uio_resid, ap->a_uio); 121 if (error) 122 return error; 123 124 if (len > 1) 125 data = ((char *)&data)[0]; 126 127 if (data >= '0') 128 data -= '0'; 129 130 lockmgr(&led_lock, LK_EXCLUSIVE); 131 gpio_pin_write(sc->gp, sc->gp_map, 0, data); 132 lockmgr(&led_lock, LK_RELEASE); 133 134 return 0; 135 } 136 137 static int 138 led_read(struct dev_read_args *ap) 139 { 140 struct ledsc *sc; 141 cdev_t dev; 142 int error; 143 int data = 0; 144 145 dev = ap->a_head.a_dev; 146 sc = dev->si_drv1; 147 148 if (ap->a_uio->uio_resid < sizeof(int)) 149 return EINVAL; 150 151 lockmgr(&led_lock, LK_EXCLUSIVE); 152 data = gpio_pin_read(sc->gp, sc->gp_map, 0); 153 lockmgr(&led_lock, LK_RELEASE); 154 155 error = uiomove((void *)&data, 156 (ap->a_uio->uio_resid > sizeof(int))?(sizeof(int)):(ap->a_uio->uio_resid), 157 ap->a_uio); 158 159 return error; 160 } 161 162 static int 163 led_ioctl(struct dev_ioctl_args *ap) 164 { 165 /* XXX: set a name */ 166 lockmgr(&led_lock, LK_EXCLUSIVE); 167 lockmgr(&led_lock, LK_RELEASE); 168 return 0; 169 } 170 171 static struct dev_ops nled_ops = { 172 { "gpio", 0, D_MPSAFE }, 173 .d_open = led_open, 174 .d_close = led_close, 175 .d_write = led_write, 176 .d_read = led_read, 177 .d_ioctl = led_ioctl, 178 }; 179 180 181 static int 182 led_attach(struct gpio *gp, void *arg, int pin, u_int32_t mask) 183 { 184 struct ledsc *sc; 185 186 if (arg == NULL) 187 return 1; 188 189 lockmgr(&led_lock, LK_EXCLUSIVE); 190 sc = kmalloc(sizeof(struct ledsc), M_LED, M_WAITOK); 191 192 /* XXX: check for name collisions */ 193 sc->name = kstrdup((char *)arg, M_LED); 194 sc->pin = pin; 195 sc->gp = gp; 196 197 sc->gp_map = gpio_map(gp, NULL, pin, 1); 198 if (sc->gp_map == NULL) { 199 lockmgr(&led_lock, LK_RELEASE); 200 return 2; 201 } 202 203 sc->unit = devfs_clone_bitmap_get(&DEVFS_CLONE_BITMAP(nled), 0); 204 205 LIST_INSERT_HEAD(&led_list, sc, list); 206 sc->dev = make_dev(&nled_ops, sc->unit, 207 UID_ROOT, GID_WHEEL, 0600, "led/%s", sc->name); 208 sc->dev->si_drv1 = sc; 209 lockmgr(&led_lock, LK_RELEASE); 210 211 kprintf("gpio_led: Attached led '%s' to gpio %s, pin %d\n", 212 sc->name, sc->gp->driver_name, pin); 213 214 return 0; 215 } 216 217 static int 218 led_detach(struct gpio *gp, void *arg, int pin) 219 { 220 /* XXX: implement */ 221 return 0; 222 } 223 224 void 225 led_switch(const char *name, int on_off) 226 { 227 struct ledsc *sc; 228 229 if (name == NULL) 230 return; 231 232 lockmgr(&led_lock, LK_EXCLUSIVE); 233 LIST_FOREACH(sc, &led_list, list) { 234 if (strcmp(name, sc->name) != 0) 235 continue; 236 237 gpio_pin_write(sc->gp, sc->gp_map, 0, on_off); 238 break; 239 } 240 241 lockmgr(&led_lock, LK_RELEASE); 242 } 243 244 struct gpio_consumer led_gpio_cons = { 245 .consumer_name = "led", 246 .consumer_attach = led_attach, 247 .consumer_detach = led_detach, 248 }; 249 250 static void 251 led_drvinit(void *unused) 252 { 253 lockinit(&led_lock, "led_lock", 0, 0); 254 devfs_clone_bitmap_init(&DEVFS_CLONE_BITMAP(nled)); 255 gpio_consumer_register(&led_gpio_cons); 256 } 257 258 SYSINIT(leddev, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, led_drvinit, NULL); 259