/* * Status LEDs driver * Copyright (c) 2012 */ #include <linux/module.h> #include <linux/types.h> #include <linux/fs.h> #include <linux/errno.h> #include <linux/mm.h> #include <linux/cdev.h> #include <linux/sched.h> #include <linux/kernel.h> #include <linux/init.h> #include <linux/spinlock.h> #include <linux/io.h> #include <linux/of.h> #include <linux/of_gpio.h> #include <linux/gpio.h> #include <asm/uaccess.h> #define DEVICE_NAME "Statusled" //定义设备名 #define LED_MAJOR 261 //定义主设备号 #define LED_MINOR 0 #define LED_MAGIC 0xBB //定义幻数 //定义操作 #define LED_ON _IOW(LED_MAGIC,1,int) #define LED_OFF _IOW(LED_MAGIC,2,int) static int led_major = LED_MAJOR; //设置主设备号 struct statusled_dev{ //define dev struct unsigned char value; struct semaphore sem; //定义信号量 struct cdev cdev; }; struct led_gpio { unsigned base; unsigned gpio; #define GPIOF_DIR_OUT 0x00000001 #define GPIOF_DIR_IN 0x00000002 #define GPIOF_INIT_LOW 0x00000004 #define GPIOF_INIT_HIGH 0x00000008 unsigned long flags; const char *label; }; static struct led_gpio leds_gpio[]={ {224,6,GPIOF_DIR_OUT | GPIOF_INIT_LOW,"RUN LED"}, {224,8,GPIOF_DIR_OUT | GPIOF_INIT_LOW,"ERRO LED"}, //{}, }; struct statusled_dev *statusled_devp = NULL; static int leds_open(struct inode *inode, struct file *filp) { struct statusled_dev *dev; int i,j; dev = container_of(inode->i_cdev, struct statusled_dev, cdev); //通过结构体成员指针找到对应结构体的指针 filp->private_data = dev; //将设备结构体指针赋给文件私有数据指针 //TODO: Status tips printk("statusled device opened\n"); return 0; } static int leds_release(struct inode *inode, struct file *filp) { int i,j; //TODO printk("statusled device closing\n"); return 0; } /* * leds_write * I want to modify all the leds status,so,there ervery one ....bits * note: don't use size.so,the max led number is 8; */ static ssize_t leds_write(struct file *filp, const char __user *buf, size_t size, loff_t *ppos) { int i; struct statusled_dev *dev = filp->private_data; int num = ARRAY_SIZE(leds_gpio); down(&dev->sem); copy_from_user(&(dev->value),buf,1); for(i=0; i < num; i++){ gpio_set_value(leds_gpio[i].base + leds_gpio[i].gpio,((dev->value >> i) & 0x01)%2); } up(&dev->sem); printk(KERN_ALERT "status LED device write ,dev->value: %d\n",dev->value); return size; } static int leds_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { if(arg > 4){ return -EINVAL; } switch (cmd) { case LED_ON: gpio_set_value(leds_gpio[arg].base + leds_gpio[arg].gpio,0); return 0; case LED_OFF: gpio_set_value(leds_gpio[arg].base + leds_gpio[arg].gpio,1); return 0; default: return -EINVAL; } } //file_operations static struct file_operations leds_fops = { .owner = THIS_MODULE, .open = leds_open, .release = leds_release, .write = leds_write, .ioctl = leds_ioctl, }; static int leds_request(struct led_gpio *leds,size_t num){ int i,ret = 0; for(i=0;i<num;i++){ ret = gpio_request(leds[i].gpio + leds[i].base,leds[i].label); if(ret){ printk("%s:request gpio %d faild\n",leds[i].label,leds[i].gpio); return ret; } if((leds[i].flags) & GPIOF_DIR_OUT) {// output if((leds[i].flags) & GPIOF_INIT_LOW){ gpio_direction_output(leds_gpio[i].base + leds[i].gpio,0); } else{ gpio_direction_output(leds_gpio[i].base + leds[i].gpio,1); } } else if((leds[i].flags) & GPIOF_DIR_IN){ //input gpio_direction_input(leds_gpio[i].base + leds[i].gpio); } else{ return -EINVAL; } } return ret; } static int leds_free(struct led_gpio *leds,size_t num){ int i,ret = 0; for(i=0; i<num;i++){ gpio_free(leds[i].base + leds[i].gpio);} return ret; } // inint cdev struct and register a char dev static void statusled_setup_cdev(struct statusled_dev *dev,int index) { int err,devno=MKDEV(led_major, LED_MINOR+index); cdev_init(&dev->cdev,&leds_fops); dev->cdev.owner = THIS_MODULE; dev->cdev.ops = &leds_fops; err = leds_request(&leds_gpio,ARRAY_SIZE(leds_gpio)); if(err){ printk(KERN_NOTICE "Error:request gpio faild \n");} err = cdev_add(&dev->cdev,devno,1); if(err) printk(KERN_NOTICE "Error %d adding statusled%d\n",err,index); } static int __init statusleds_init(void) { int result,i,j; dev_t devno = MKDEV(led_major, LED_MINOR); //创建设备号 //注册设备号 if (led_major) { result = register_chrdev_region(devno, 1, DEVICE_NAME); } else { result = alloc_chrdev_region(&devno, LED_MINOR, 1, DEVICE_NAME); led_major = MAJOR(devno); } if (result < 0) { printk("Can't register\n"); return result; } statusled_devp = kmalloc(sizeof(struct statusled_dev), GFP_KERNEL); if (!statusled_devp) { result = -ENOMEM; unregister_chrdev_region(devno, 1); return result; } memset(statusled_devp, 0, sizeof(struct statusled_dev)); statusled_setup_cdev(statusled_devp,0); init_MUTEX(&statusled_devp->sem); printk("Status LEDs Function is enable\n"); return 0; } static void __exit statusleds_exit(void){ int i,j; leds_free(&leds_gpio,ARRAY_SIZE(leds_gpio)); cdev_del(&statusled_devp->cdev); kfree(statusled_devp); unregister_chrdev_region(MKDEV(led_major, LED_MINOR), 1); printk("Status LEDs Function is disable\n"); } module_init(statusleds_init); module_exit(statusleds_exit); MODULE_AUTHOR("Caijun"); MODULE_DESCRIPTION("Status LEDs Driver"); // 一些描述信息 MODULE_LICENSE("GPL");