Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- /*
- * extio.c
- *
- * Copyright (C) 2011 [scolopender] (scolopender@arcor.de)
- *
- * Basics adopted from "Linux Device Drivers" by Alessandro Rubini and
- * Jonathan Corbet, published by O'Reilly & Associates.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
- #include <linux/config.h>
- #include <linux/types.h>
- #include <linux/module.h>
- #include <linux/moduleparam.h>
- #include <linux/init.h>
- #include <linux/kernel.h>
- #include <linux/fs.h>
- #include <linux/errno.h>
- #include <linux/fcntl.h>
- #include <linux/cdev.h>
- #include <asm/semaphore.h>
- #include <asm/system.h>
- #include <asm/uaccess.h>
- #include <bcm_map_part.h>
- #include <bcmtypes.h>
- #define EXTIO_NAME "extio"
- #define EXTIO_COUNT 3
- #define EXTIO_MAJOR 200
- #define EXTIO_MINOR 0
- /* port direction */
- #define EXTIO_DIR_IN 0UL
- #define EXTIO_DIR_OUT 1UL
- /* sinal level */
- #define EXTIO_ACT_HI 0UL
- #define EXTIO_ACT_LO 1UL
- /* relay K1, output */
- #define EXTIO_OUT_0 0
- #define EXTIO_GPIO_0 7
- #define EXTIO_DIR_0 EXTIO_DIR_OUT
- #define EXTIO_ACT_0 EXTIO_ACT_LO
- #define EXTIO_SHIFT_0 EXTIO_GPIO_0
- #define EXTIO_GPIO_DIR_0 GPIO->GPIODir
- #define EXTIO_GPIO_PORT_0 GPIO->GPIOio
- /* relay K2, output */
- #define EXTIO_OUT_1 1
- #define EXTIO_GPIO_1 35
- #define EXTIO_DIR_1 EXTIO_DIR_OUT
- #define EXTIO_ACT_1 EXTIO_ACT_LO
- #define EXTIO_SHIFT_1 (EXTIO_GPIO_1 - 32)
- #define EXTIO_GPIO_DIR_1 GPIO->GPIODir_high
- #define EXTIO_GPIO_PORT_1 GPIO->GPIOio_high
- /* photocoupler PC1, input */
- #define EXTIO_IN_2 2
- #define EXTIO_GPIO_2 27
- #define EXTIO_DIR_2 EXTIO_DIR_IN
- #define EXTIO_ACT_2 EXTIO_ACT_LO
- #define EXTIO_SHIFT_2 EXTIO_GPIO_2
- #define EXTIO_GPIO_DIR_2 GPIO->GPIODir
- #define EXTIO_GPIO_PORT_2 GPIO->GPIOio
- /* convenience */
- typedef struct cdev CDEV, *CDEV_P;
- typedef struct file FILE_, *FILE_P;
- typedef struct inode INODE, *INODE_P;
- typedef struct file_operations F_OPS, *F_OPS_P;
- typedef struct semaphore SEMA, *SEMA_P;
- /* GPIO descriptor */
- struct extio_gpio_s
- {
- volatile uint32 *dir_p;
- volatile uint32 *port_p;
- uint32 mask;
- uint32 level;
- uint32 dir;
- };
- typedef struct extio_gpio_s EXTIO_GPIO, *EXTIO_GPIO_P;
- typedef const EXTIO_GPIO *C_EXTIO_GPIO_P;
- struct extio_driver_s
- {
- C_EXTIO_GPIO_P c_eg_p;
- int eof_flag;
- SEMA sema;
- CDEV_P cdev_p;
- };
- typedef struct extio_driver_s EXTIO_DRIVER, *EXTIO_DRIVER_P;
- MODULE_AUTHOR("scolopender");
- MODULE_LICENSE("Proprietary");
- const char kern_alert[] = KERN_ALERT;
- const char kern_warning[] = KERN_WARNING;
- const char extio_name[] = EXTIO_NAME;
- /* GPIO descriptor table */
- const EXTIO_GPIO extio_gpio[EXTIO_COUNT] =
- {
- /* relay K1 */
- {
- (volatile uint32 *)&(EXTIO_GPIO_DIR_0),
- (volatile uint32 *)&(EXTIO_GPIO_PORT_0),
- (1UL << EXTIO_SHIFT_0),
- (EXTIO_ACT_0 << EXTIO_SHIFT_0),
- (EXTIO_DIR_0 << EXTIO_SHIFT_0),
- },
- /* relay K2 */
- {
- (volatile uint32 *)&(EXTIO_GPIO_DIR_1),
- (volatile uint32 *)&(EXTIO_GPIO_PORT_1),
- (1UL << EXTIO_SHIFT_1),
- (EXTIO_ACT_1 << EXTIO_SHIFT_1),
- (EXTIO_DIR_1 << EXTIO_SHIFT_1),
- },
- /* photocoupler PC1 */
- {
- (volatile uint32 *)&(EXTIO_GPIO_DIR_2),
- (volatile uint32 *)&(EXTIO_GPIO_PORT_2),
- (1UL << EXTIO_SHIFT_2),
- (EXTIO_ACT_2 << EXTIO_SHIFT_2),
- (EXTIO_DIR_2 << EXTIO_SHIFT_2),
- }
- };
- /* module parameters, default */
- int extio_major = EXTIO_MAJOR;
- int extio_minor = EXTIO_MINOR;
- module_param(extio_major, int, S_IRUGO);
- module_param(extio_minor, int, S_IRUGO);
- dev_t extio_dev = 0;
- EXTIO_DRIVER extio_driver[EXTIO_COUNT];
- /* get GPIO pin state */
- static char get_gpio(C_EXTIO_GPIO_P c_eg_p)
- {
- return( ( ((*(c_eg_p->port_p) & c_eg_p->mask) ^ c_eg_p->level) != 0 ) ? '1' : '0' );
- }
- /* change GPIO pin state */
- static int put_gpio(C_EXTIO_GPIO_P c_eg_p, char state)
- {
- uint32 mask, level, port;
- volatile uint32 *port_p;
- mask = c_eg_p->mask;
- level = c_eg_p->level;
- port_p = c_eg_p->port_p;
- port = *port_p ^ level;
- /* assert / release */
- switch ( state )
- {
- case '0' :
- {
- /* release gpio */
- port &= ~mask;
- break;
- } /* '0' */
- case '1' :
- {
- /* assert gpio */
- port |= mask;
- break;
- } /* '1' */
- default :
- {
- return(-EINVAL);
- }
- }
- *port_p = port ^ level;
- return(0);
- } /* put_gpio */
- /* "device" read */
- static ssize_t extio_read(FILE_P file_p, char __user *buf_p, size_t count, loff_t *loff_p)
- {
- EXTIO_DRIVER_P ed_p;
- C_EXTIO_GPIO_P c_eg_p;
- ssize_t retval;
- char data;
- ed_p = file_p->private_data;
- if ( down_interruptible(&(ed_p->sema)) )
- {
- return(-ERESTARTSYS);
- }
- c_eg_p = ed_p->c_eg_p;
- /* one byte only */
- if ( count > 1 )
- {
- if ( ed_p->eof_flag != 0 )
- {
- count = 0;
- }
- else
- {
- count = 1;
- ed_p->eof_flag = 1;
- }
- }
- if ( count != 0 )
- {
- /* get port state */
- data = get_gpio(c_eg_p);
- /* copy to user */
- retval = put_user(data, buf_p);
- if ( retval != 0 )
- {
- goto out;
- }
- }
- retval = count;
- out :
- up(&(ed_p->sema));
- return(retval);
- } /* extio_read */
- /* "device" write */
- static ssize_t extio_write(FILE_P file_p, const char __user *buf_p, size_t count, loff_t *loff_p)
- {
- EXTIO_DRIVER_P ed_p;
- C_EXTIO_GPIO_P c_eg_p;
- ssize_t retval;
- char data;
- ed_p = file_p->private_data;
- if ( down_interruptible(&(ed_p->sema)) )
- {
- return(-ERESTARTSYS);
- }
- c_eg_p = ed_p->c_eg_p;
- /* first byte only */
- /* copy from user */
- retval = get_user(data, buf_p);
- if ( retval != 0 )
- {
- goto out;
- }
- /* assert / release */
- retval = put_gpio(c_eg_p, data);
- if ( retval != 0 )
- {
- goto out;
- }
- retval = count;
- out :
- up(&(ed_p->sema));
- return(retval);
- } /* extio_write */
- /* "device" open */
- static int extio_open(INODE_P inode_p, FILE_P file_p)
- {
- EXTIO_DRIVER_P ed_p;
- ed_p = &(extio_driver[iminor(inode_p) - extio_minor]);
- file_p->private_data = ed_p;
- ed_p->eof_flag = 0;
- /* always success */
- return(0);
- } /* extio_open */
- /* "device" close */
- static int extio_release(INODE_P inode_p, FILE_P file_p)
- {
- /* always success */
- return(0);
- } /* extio_release */
- /* "methods" */
- struct file_operations extio_fops =
- {
- .owner = THIS_MODULE,
- .read = extio_read,
- .write = extio_write,
- .open = extio_open,
- .release = extio_release,
- };
- static void extio_cleanup(void)
- {
- EXTIO_DRIVER_P ed_p;
- int index;
- for ( index = 0, ed_p = extio_driver; index < EXTIO_COUNT; index++, ed_p++ )
- {
- if ( ed_p->cdev_p != NULL )
- {
- cdev_del(ed_p->cdev_p);
- kfree(ed_p->cdev_p);
- ed_p->cdev_p = NULL;
- }
- }
- if ( extio_dev != 0 )
- {
- unregister_chrdev_region(extio_dev, EXTIO_COUNT);
- extio_dev = 0;
- }
- } /* extio_cleanup */
- /* module dtor */
- static void __exit extio_exit(void)
- {
- extio_cleanup();
- printk("%s%s stopped !\n", kern_alert, extio_name);
- } /* extio_exit */
- /* module ctor */
- static int __init extio_init(void)
- {
- EXTIO_DRIVER_P ed_p;
- C_EXTIO_GPIO_P c_eg_p;
- dev_t dev;
- int index, result;
- uint32 mask;
- volatile uint32 *port_p, *dir_p;
- /* parameter check */
- if ( (extio_major < 0) || (extio_major > 255) )
- {
- printk("%s%s: parameter 'major' invalid\n", kern_alert, extio_name);
- return(-EINVAL);
- }
- /* parameter check */
- if ( (extio_minor < 0) || (extio_minor > (256 - EXTIO_COUNT)) )
- {
- printk("%s%s: parameter 'minor' invalid\n", kern_alert, extio_name);
- return(-EINVAL);
- }
- /* storage preset */
- memset(extio_driver, 0, sizeof(extio_driver));
- /* device number */
- extio_dev = MKDEV(extio_major, extio_minor);
- result = register_chrdev_region(extio_dev, EXTIO_COUNT, (char *)extio_name);
- if ( result < 0 )
- {
- printk("%s%s: cannot get major %d\n", kern_warning, extio_name, MAJOR(extio_dev));
- extio_dev = 0;
- return(result);
- }
- /* char device allocation */
- for ( index = 0, ed_p = extio_driver, c_eg_p = extio_gpio; index < EXTIO_COUNT; index++, ed_p++, c_eg_p++ )
- {
- ed_p->cdev_p = cdev_alloc();
- if ( ed_p->cdev_p == NULL )
- {
- printk("%s%s%d: cannot get memory for cdev struct\n", kern_warning, extio_name, index);
- extio_cleanup();
- return(-ENOMEM);
- }
- /* semaphore setup */
- init_MUTEX(&(ed_p->sema));
- /* char device init */
- cdev_init(ed_p->cdev_p, &extio_fops);
- ed_p->cdev_p->owner = THIS_MODULE;
- ed_p->cdev_p->ops = &extio_fops;
- /* GPIO descriptor */
- ed_p->c_eg_p = c_eg_p;
- /* gpio setup */
- mask = c_eg_p->mask;
- port_p = c_eg_p->port_p;
- dir_p = c_eg_p->dir_p;
- if ( c_eg_p->dir == 0 )
- {
- /* configure for input */
- *dir_p &= ~mask;
- }
- else
- {
- /* configure for output */
- *dir_p |= mask;
- /* initially release gpio */
- if ( c_eg_p->level == 0 )
- {
- *port_p &= ~mask;
- }
- else
- {
- *port_p |= mask;
- }
- }
- /* driver start */
- dev = MKDEV(extio_major, extio_minor + index);
- result = cdev_add(ed_p->cdev_p, dev, 1);
- if ( result < 0 )
- {
- printk("%s%s%d: cannot add device %d\n", kern_warning, extio_name, index, extio_minor + index);
- kfree(ed_p->cdev_p);
- ed_p->cdev_p = NULL;
- extio_cleanup();
- return(result);
- }
- }
- printk("%s%s started !\n", kern_alert, extio_name);
- return(0);
- } /* extio_init */
- module_init(extio_init);
- module_exit(extio_exit);
- /* EoF */
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement