/* * GPIO char driver * * Copyright (C) 2011, Broadcom Corporation. All Rights Reserved. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * $Id: gpio.c,v 1.5 2008-04-03 03:49:45 $ */ #include #include #include #include #include #include #include #include #include static si_t *gpio_sih; static int gpio_major; static devfs_handle_t gpio_dir; static struct { char *name; devfs_handle_t handle; } gpio_file[] = { { "in", NULL }, { "out", NULL }, { "outen", NULL }, { "control", NULL } }; static int gpio_open(struct inode *inode, struct file * file) { if (MINOR(inode->i_rdev) > ARRAYSIZE(gpio_file)) return -ENODEV; MOD_INC_USE_COUNT; return 0; } static int gpio_release(struct inode *inode, struct file * file) { MOD_DEC_USE_COUNT; return 0; } static ssize_t gpio_read(struct file *file, char *buf, size_t count, loff_t *ppos) { u32 val; switch (MINOR(file->f_dentry->d_inode->i_rdev)) { case 0: val = si_gpioin(gpio_sih); break; case 1: val = si_gpioout(gpio_sih, 0, 0); break; case 2: val = si_gpioouten(gpio_sih, 0, 0); break; case 3: val = si_gpiocontrol(gpio_sih, 0, 0); break; default: return -ENODEV; } if (put_user(val, (u32 *) buf)) return -EFAULT; return sizeof(val); } static ssize_t gpio_write(struct file *file, const char *buf, size_t count, loff_t *ppos) { u32 val; if (get_user(val, (u32 *) buf)) return -EFAULT; switch (MINOR(file->f_dentry->d_inode->i_rdev)) { case 0: return -EACCES; case 1: si_gpioout(gpio_sih, ~0, val); break; case 2: si_gpioouten(gpio_sih, ~0, val); break; case 3: si_gpiocontrol(gpio_sih, ~0, val); break; default: return -ENODEV; } return sizeof(val); } static struct file_operations gpio_fops = { owner: THIS_MODULE, open: gpio_open, release: gpio_release, read: gpio_read, write: gpio_write, }; static int __init gpio_init(void) { int i; if (!(gpio_sih = si_kattach(SI_OSH))) return -ENODEV; si_gpiosetcore(gpio_sih); if ((gpio_major = devfs_register_chrdev(0, "gpio", &gpio_fops)) < 0) return gpio_major; gpio_dir = devfs_mk_dir(NULL, "gpio", NULL); for (i = 0; i < ARRAYSIZE(gpio_file); i++) { gpio_file[i].handle = devfs_register(gpio_dir, gpio_file[i].name, DEVFS_FL_DEFAULT, gpio_major, i, S_IFCHR | S_IRUGO | S_IWUGO, &gpio_fops, NULL); } return 0; } static void __exit gpio_exit(void) { int i; for (i = 0; i < ARRAYSIZE(gpio_file); i++) devfs_unregister(gpio_file[i].handle); devfs_unregister(gpio_dir); devfs_unregister_chrdev(gpio_major, "gpio"); si_detach(gpio_sih); } module_init(gpio_init); module_exit(gpio_exit);