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/cdefs.h> 36 37 #include <sys/param.h> 38 #include <sys/conf.h> 39 #include <sys/kernel.h> 40 #include <sys/systm.h> 41 #include <sys/limits.h> 42 #include <sys/malloc.h> 43 #include <sys/ctype.h> 44 #include <sys/sbuf.h> 45 #include <sys/queue.h> 46 #include <dev/misc/gpio/gpio.h> 47 #include <sys/uio.h> 48 #include <sys/lock.h> 49 #include <sys/devfs.h> 50 51 struct ledsc { 52 LIST_ENTRY(ledsc) list; 53 struct gpio *gp; 54 int pin; 55 cdev_t dev; 56 int unit; 57 int opened; 58 char *name; 59 struct gpio_mapping *gp_map; 60 }; 61 62 DEVFS_DECLARE_CLONE_BITMAP(nled); 63 static struct lock led_lock; 64 static LIST_HEAD(, ledsc) led_list = LIST_HEAD_INITIALIZER(&led_list); 65 static MALLOC_DEFINE(M_LED, "LED", "LED driver"); 66 67 68 static int 69 led_open(struct dev_open_args *ap) 70 { 71 struct ledsc *sc; 72 cdev_t dev; 73 74 dev = ap->a_head.a_dev; 75 sc = dev->si_drv1; 76 77 if (sc->opened) 78 return EBUSY; 79 80 sc->opened = 1; 81 82 return 0; 83 } 84 85 static int 86 led_close(struct dev_close_args *ap) 87 { 88 struct ledsc *sc; 89 cdev_t dev; 90 91 dev = ap->a_head.a_dev; 92 sc = dev->si_drv1; 93 94 if (sc->opened) 95 sc->opened = 0; 96 97 return 0; 98 } 99 100 static int 101 led_write(struct dev_write_args *ap) 102 { 103 struct ledsc *sc; 104 cdev_t dev; 105 int error; 106 int data = 0; 107 int len; 108 109 dev = ap->a_head.a_dev; 110 sc = dev->si_drv1; 111 112 if (ap->a_uio->uio_resid > sizeof(int)) 113 return EINVAL; 114 115 len = ap->a_uio->uio_resid; 116 117 error = uiomove((void *)&data, ap->a_uio->uio_resid, ap->a_uio); 118 if (error) 119 return error; 120 121 if (len > 1) 122 data = ((char *)&data)[0]; 123 124 if (data >= '0') 125 data -= '0'; 126 127 gpio_pin_write(sc->gp, sc->gp_map, 0, data); 128 129 return 0; 130 } 131 132 static int 133 led_read(struct dev_read_args *ap) 134 { 135 struct ledsc *sc; 136 cdev_t dev; 137 int error; 138 int data = 0; 139 140 dev = ap->a_head.a_dev; 141 sc = dev->si_drv1; 142 143 if (ap->a_uio->uio_resid < sizeof(int)) 144 return EINVAL; 145 146 data = gpio_pin_read(sc->gp, sc->gp_map, 0); 147 148 error = uiomove((void *)&data, 149 (ap->a_uio->uio_resid > sizeof(int))?(sizeof(int)):(ap->a_uio->uio_resid), 150 ap->a_uio); 151 152 return error; 153 } 154 155 static int 156 led_ioctl(struct dev_ioctl_args *ap) 157 { 158 /* XXX: set a name */ 159 return 0; 160 } 161 162 static struct dev_ops nled_ops = { 163 { "gpio", 0, 0 }, 164 .d_open = led_open, 165 .d_close = led_close, 166 .d_write = led_write, 167 .d_read = led_read, 168 .d_ioctl = led_ioctl, 169 }; 170 171 172 static int 173 led_attach(struct gpio *gp, void *arg, int pin, u_int32_t mask) 174 { 175 struct ledsc *sc; 176 177 if (arg == NULL) 178 return 1; 179 180 lockmgr(&led_lock, LK_EXCLUSIVE); 181 sc = kmalloc(sizeof(struct ledsc), M_LED, M_WAITOK); 182 183 /* XXX: check for name collisions */ 184 sc->name = kstrdup((char *)arg, M_LED); 185 sc->pin = pin; 186 sc->gp = gp; 187 188 sc->gp_map = gpio_map(gp, NULL, pin, 1); 189 if (sc->gp_map == NULL) { 190 return 2; 191 } 192 193 sc->unit = devfs_clone_bitmap_get(&DEVFS_CLONE_BITMAP(nled), 0); 194 195 LIST_INSERT_HEAD(&led_list, sc, list); 196 sc->dev = make_dev(&nled_ops, sc->unit, 197 UID_ROOT, GID_WHEEL, 0600, "led/%s", sc->name); 198 sc->dev->si_drv1 = sc; 199 lockmgr(&led_lock, LK_RELEASE); 200 201 kprintf("gpio_led: Attached led '%s' to gpio %s, pin %d\n", 202 sc->name, sc->gp->driver_name, pin); 203 204 return 0; 205 } 206 207 static void 208 led_detach(struct gpio *gp, void *arg, int pin) 209 { 210 /* XXX: implement */ 211 } 212 213 void 214 led_switch(const char *name, int on_off) 215 { 216 struct ledsc *sc; 217 218 if (name == NULL) 219 return; 220 221 lockmgr(&led_lock, LK_EXCLUSIVE); 222 LIST_FOREACH(sc, &led_list, list) { 223 if (strcmp(name, sc->name) != 0) 224 continue; 225 226 gpio_pin_write(sc->gp, sc->gp_map, 0, on_off); 227 break; 228 } 229 230 lockmgr(&led_lock, LK_RELEASE); 231 } 232 233 struct gpio_consumer led_gpio_cons = { 234 .consumer_name = "led", 235 .consumer_attach = led_attach, 236 .consumer_detach = led_detach, 237 }; 238 239 static void 240 led_drvinit(void *unused) 241 { 242 lockinit(&led_lock, "led_lock", 0, 0); 243 devfs_clone_bitmap_init(&DEVFS_CLONE_BITMAP(nled)); 244 gpio_consumer_register(&led_gpio_cons); 245 } 246 247 SYSINIT(leddev, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, led_drvinit, NULL); 248