天天看點

LED驅動執行個體2

#include <linux/module.h>

#include <linux/kernel.h>

#include <linux/fs.h>

#include <linux/proc_fs.h>

#include <linux/init.h>

#include <linux/delay.h>

#include <asm/irq.h>

#include <asm/arch/regs-gpio.h>

#include <asm/hardware.h>

#include <asm/uaccess.h>

#define DEVICE_NAME     "leds" 

#define LED_MAJOR       231    

#define IOCTL_LED_ON    0

#define IOCTL_LED_OFF   1

static unsigned long led_table [] = {

    S3C2410_GPF7,

    S3C2410_GPF6,

    S3C2410_GPF5,

    S3C2410_GPF4,

};

static unsigned int led_cfg_table [] = {

    S3C2410_GPF7_OUTP,

    S3C2410_GPF6_OUTP,

    S3C2410_GPF5_OUTP,

    S3C2410_GPF4_OUTP,

};

#undef NDEBUG

#define NDEBUG

#undef PDEBUG

#define VERBOSE

#ifdef NDEBUG

    #ifdef __KERNEL__

        #ifdef VERBOSE

            #define PDEBUG(fmt, args...) printk( KERN_ERR "%s:%d " fmt, __FILE__, __LINE__, ## args)

        #else

            #define PDEBUG(fmt, args...) printk( KERN_ERR "kdebug: " fmt, ## args)

        #endif

    #else

        #define PDEBUG(fmt, args...) fprintf(stderr, "%s:%d " fmt,  __FILE__, __LINE__, ## args)

    #endif

#else

    #define PDEBUG(fmt, args...) do {} while(0)

#endif

static int s3c24xx_leds_open(struct inode *inode, struct file *file)

{

    int i;

    for (i = 0; i < 4; i++) {

        // 設定GPIO引腳的功能:本驅動中LED所涉及的GPIO引腳設為輸出功能

        s3c2410_gpio_cfgpin(led_table[i], led_cfg_table[i]);

     // 關閉内部上拉

     s3c2410_gpio_pullup(led_table[i], 1);

    }

    return 0;

}

static int s3c24xx_leds_ioctl(

    struct inode *inode,

    struct file *file,

    unsigned int cmd,

    unsigned long arg)

{

    if (arg > 4) {

        return -EINVAL;

    }

    switch(cmd) {

    case IOCTL_LED_ON:

        // 設定指定引腳的輸出電平為0V

        s3c2410_gpio_setpin(led_table[arg], 0);

        return 0;

    case IOCTL_LED_OFF:

        // 設定指定引腳的輸出電平為3.3V

        s3c2410_gpio_setpin(led_table[arg], 1);

        return 0;

    default:

        return -EINVAL;

    }

}

static inline void s3c24xx_leds_proc_usage(char *exename)

{

    printk(KERN_ERR "Usage:\n");

    printk(KERN_ERR "echo \"<led_no> <on/off>\" > /proc/%s\n", exename);

    printk(KERN_ERR "led_no = 1, 2, 3 or 4\n");

}

static ssize_t s3c24xx_leds_proc_write (struct file *file,

   const char __user *buffer, size_t count, loff_t *offset)

{

 char *buf, *tok, **tokptrs;

 char *whitespace = " ,\t\r\n"; //空格和","分隔

 int ret = -EINVAL, leds_state, leds_no = -1, ntoks;

 int i = 0;

 if (!capable(CAP_SYS_ADMIN))

  return -EPERM;

 if (count == 0)

  return 0;

 if (!(buf = kmalloc(count + 1, GFP_KERNEL)))

  return -ENOMEM;

 if (copy_from_user((void *)buf, buffer, count)) {

  ret = -EFAULT;

  goto out0;

 }

 buf[count] = '\0';

 PDEBUG("s3c24xx_leds: buf= %s\n", buf);

 if (!(tokptrs = (char **)get_zeroed_page(GFP_KERNEL))) {

  ret = -ENOMEM;

  goto out1;

 }

 ret = -EINVAL;

 ntoks = 0;

 do {

  buf = buf + strspn(buf, whitespace);

  tok = strsep(&buf, whitespace);

  if (*tok == '\0') {

   if (ntoks == 0) {

    goto out1;

   } else

    break;

  }

  PDEBUG("in do_while loop%d buf=%s, tok=%s\n",ntoks, buf, tok);

  if (ntoks == (PAGE_SIZE / sizeof(char **)))

   goto out1;

  tokptrs[ntoks++] = tok;

 } while(buf);

 PDEBUG("s3c24xx_leds: ntoks= %d, buf2= %s\n", ntoks, buf);

 if (ntoks <2)

   goto out1;

 leds_state = -1;

 //The following two lines just for debug

 i= strcmp(tokptrs[ntoks-1], "on");

 PDEBUG("s3c24xx_leds states:%s,strcmp result=%d\n", tokptrs[ntoks-1],i);

 if (strcmp(tokptrs[ntoks-1], "on")==0) {

  leds_state = 1;

 } else if (strcmp(tokptrs[ntoks-1], "off") == 0) {

  leds_state = 0; 

 } else {

  goto out1;

 }

 for (i = 0; i< ntoks-1; i++) {

     leds_no = simple_strtoul(tokptrs[i], NULL, 0);

  PDEBUG("operate led-%d\n", leds_no);

  if(leds_no < 1 || leds_no >4) {

    PDEBUG("Error leds number %d\n", leds_no);

    s3c24xx_leds_proc_usage(DEVICE_NAME);

    continue;

  }

  s3c2410_gpio_setpin(led_table[leds_no-1], 1-leds_state); // LED ON or OFF

  ret = 0;

 }

out1:

 free_page((unsigned long)tokptrs);

out0:

 kfree(buf);

 if (ret == 0)

  return count;

 else {

   s3c24xx_leds_proc_usage(DEVICE_NAME); 

  return ret;

 }

}

static struct file_operations s3c24xx_leds_fops = {

    .owner  =   THIS_MODULE,   

    .open   =   s3c24xx_leds_open,    

    .ioctl  =   s3c24xx_leds_ioctl,

};

static struct file_operations s3c24xx_leds_proc_ops = {

    .owner = THIS_MODULE,

    .write = s3c24xx_leds_proc_write,

};

static int __init s3c24xx_leds_init(void)

{

    int ret;

    struct proc_dir_entry *entry;

    ret = register_chrdev(LED_MAJOR, DEVICE_NAME, &s3c24xx_leds_fops);

    if (ret < 0) {

      printk(DEVICE_NAME " can't register major number\n");

      return ret;

    }

    entry = create_proc_entry(DEVICE_NAME, S_IFREG | S_IRUGO, NULL);

    if (!entry)

        return -ENOMEM;

    entry->proc_fops = &s3c24xx_leds_proc_ops;

    printk(DEVICE_NAME " initialized\n");

    PDEBUG("LEDS INIT\n");

 return 0;

}

static void __exit s3c24xx_leds_exit(void)

{

   remove_proc_entry(DEVICE_NAME, NULL);  //與create_proc_entry函數相反

   unregister_chrdev(LED_MAJOR, DEVICE_NAME);

}

module_init(s3c24xx_leds_init);

module_exit(s3c24xx_leds_exit);

MODULE_AUTHOR("StephenYee, Shenzhen Office, Farsight Inc.,");             // 驅動程式的作者

MODULE_DESCRIPTION("S3C2410/S3C2440 LED Driver");   // 一些描述資訊

MODULE_LICENSE("GPL");                              // 遵循的協定

應用程式:

#include <stdio.h>

#include <stdlib.h>

#include <unistd.h>

#include <sys/ioctl.h>

#define IOCTL_LED_ON    0

#define IOCTL_LED_OFF   1

void usage(char *exename)

{

    printf("Usage:\n");

    printf("    %s <led_no> <on/off>\n", exename);

    printf("    led_no = 1, 2, 3 or 4\n");

}

int main(int argc, char **argv)

{

    unsigned int led_no;

    int fd = -1;

    if (argc != 3)

        goto err;

    fd = open("/dev/leds", 0);  // 打開裝置

    if (fd < 0) {

        printf("Can't open /dev/leds\n");

        return -1;

    }

    led_no = strtoul(argv[1], 0, 0) - 1;    // 操作哪個LED?

    if (led_no > 3)

        goto err;

    if (!strcmp(argv[2], "on")) {

        ioctl(fd, IOCTL_LED_ON, led_no);    // 點亮它

    } else if (!strcmp(argv[2], "off")) {

        ioctl(fd, IOCTL_LED_OFF, led_no);   // 熄滅它

    } else {

        goto err;

    }

    close(fd);

    return 0;

err:

    if (fd > 0)

        close(fd);

    usage(argv[0]);

    return -1;

}