原创 linux下LED驱动(使用register_chrdev_region)

2010-3-7 19:30 6993 6 6 分类: MCU/ 嵌入式
本来使用register_chrdev()注册,看编译之下2.6.30已经对它不支持拉。转而用register_chrdev_region().

驱动文件(module)
/***********************
  name: leds.c
  copyright: loop
  build date: 2010.3.7
************************/


#include <linux/miscdevice.h>
#include <linux/delay.h>
#include <asm/irq.h>
#include <mach/regs-gpio.h>
#include <mach/hardware.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/mm.h>
#include <linux/fs.h>
#include <linux/types.h>
#include <linux/delay.h>
#include <linux/moduleparam.h>
#include <linux/slab.h>
#include <linux/errno.h>
#include <linux/ioctl.h>
#include <linux/cdev.h>
#include <linux/string.h>
#include <linux/list.h>
#include <linux/pci.h>
#include <asm/uaccess.h>
#include <asm/atomic.h>
#include <asm/unistd.h>

#define DEVICE_NAME "led"
#define LED_MAJOR  200

/* 用来指定LED所用的GPIO引脚 */
static unsigned long led_table [] =
{
    S3C2410_GPB5,
    S3C2410_GPB6,
    S3C2410_GPB7,
    S3C2410_GPB8,
};

/* 用来指定GPIO引脚的功能:输出 */
static unsigned int led_cfg_table [] =
{
    S3C2410_GPB5_OUTP,
    S3C2410_GPB6_OUTP,
    S3C2410_GPB7_OUTP,
    S3C2410_GPB8_OUTP,
};

static int Led_open(
    struct inode *inode,
    struct file  *file)
{
    int i;
    for (i=0; i<4; i++)
    {
        s3c2410_gpio_cfgpin(led_table, led_cfg_table);
    }
    return 0;
}

static int Led_ioctl(
    struct inode *inode,
    struct file *file,
    unsigned int cmd,
    unsigned long arg)
{
    if (arg > 4)
    {
        return -EINVAL;
    }

    switch(cmd)
    {
        case 0:
            // 设置指定引脚的输出电平为0
            s3c2410_gpio_setpin(led_table[arg], 0);
            return 0;

        case 1:
            // 设置指定引脚的输出电平为1
            s3c2410_gpio_setpin(led_table[arg], 1);
            return 0;

        default:
            return -EINVAL;
    }
}

static struct file_operations leds_fops = {
    .owner    =    THIS_MODULE,
    .open   =       Led_open,
    .ioctl    =    Led_ioctl,
};

struct led_dev_g
{
    struct cdev cdev;
}led_dev;

static void Led_setup_cdev(void)
{
    int err, devno = MKDEV(LED_MAJOR, 0);
    cdev_init(&led_dev.cdev, &leds_fops);
    led_dev.cdev.owner = THIS_MODULE;
    led_dev.cdev.ops   = &leds_fops;
   
    err = cdev_add(&led_dev.cdev, devno, 1);

    if (err)
        printk("Error %d\n", err);
    else
        printk("have finish add\n");
}


static int __init Led_init(void)
{
    int ret;
    dev_t devno = MKDEV(LED_MAJOR, 0);
    ret = register_chrdev_region(devno, 1, DEVICE_NAME);   
   
    if (ret < 0)
    {
        printk (DEVICE_NAME " can't register\n");   
        return ret;   
    }
   
    Led_setup_cdev();
    printk (DEVICE_NAME " Initialized \n");
    return 0;
}

static void __exit Led_exit(void)
{   
    cdev_del(&led_dev.cdev);
    unregister_chrdev_region(MKDEV(LED_MAJOR, 0), 1);
    printk(DEVICE_NAME " exit\n");
}

module_init(Led_init);
module_exit(Led_exit);
 
MODULE_LICENSE("GPL");
MODULE_AUTHOR("loop zhong");
MODULE_DESCRIPTION("GPIO control for EmbedSky SKY2440/TQ2440 Board");


用户空间,测试程序
/***************
name : test_led
 **************/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ioctl.h>

int main(int argc, char **argv)
{
    int i;
    int led_no;
    int fd;
    if (argc != 2 || sscanf(argv[1], "%d", &led_no) != 1 ||
         led_no < 0 || led_no > 1) {
        fprintf(stderr, "Usage: test_led 0|1\n");
        exit(1);
    }
    fd = open("/dev/led_for_loop", 0);
    if (fd < 0) {
        perror("open device led");
        exit(1);
    }
   
    for (i=0; i<4; i++)
    {
        ioctl(fd, 1, i);
    }
       
    printf("now let's go with the led trip, come on\n");
    sleep(3);
    if (led_no)
    {
        for (i=0; i<4; i++)
        {
            ioctl(fd, 0, i);
            sleep(3);
            ioctl(fd, 1, i);
        }       
    }
    else
    {
        for (i=0; i<4; i++)
        {
            ioctl(fd, 0, 3-i);
            sleep(3);
            ioctl(fd, 1, 3-i);
        }       
    }

    printf("performan is over, thank you\n");
    close(fd);
    return 0;
}

-------------------------------
之后
#insmod leds.ko
#lsmod                (可以看到leds模块已经添加; 但在/dev下看不到 设备名为led的)

#mknod   /dev/led_for_loop   c   200  0   (这个主,次设备号必须与leds.c 文件定义的一致,设备名字则可以任意改)

#test_led  1 (即可以驱动led拉)

-----------------------------
对比用misc_register()来注册。



static struct file_operations dev_fops = {

           .owner = THIS_MODULE,

           .ioctl = tq2440_gpio_ioctl,

};

 static struct miscdevice misc = {

           .minor = MISC_DYNAMIC_MINOR,

           .name = DEVICE_NAME,

           .fops = &dev_fops,
};

 static int __init dev_init(void)

{
           int ret;


           int i;
          for (i = 0; i < 4; i++)
          {
                   s3c2410_gpio_cfgpin(gpio_table, gpio_cfg_table);                                   
                   s3c2410_gpio_setpin(gpio_table, 0);
          }
           ret = misc_register(&misc);


           printk ("retrun value is %d\n", ret);
           printk (DEVICE_NAME" initialized\n");
          return ret;

}

以下摘自http://hi.baidu.com/pnalson/blog/item/02208d6ec0cddad080cb4a6c.html

杂项设备(misc device)

杂项设备也是在嵌入式系统中用得比较多的一种设备驱动。在 Linux 内核的include\linux目录下有Miscdevice.h文件,要把自己定义的misc device从设备定义在这里。其实是因为这些字符设备不符合预先确定的字符设备范畴,所有这些设备采用主编号10 ,一起归于misc device,其实misc_register就是用主标号10调用register_chrdev()的。


也就是说,misc设备其实也就是特殊的字符设备。





字符设备(char device)


使用
register_chrdev(LED_MAJOR,DEVICE_NAME,&dev_fops)注册字符设备驱动程序时,如果有多个设
备使用该函数注册驱动程序,LED_MAJOR不能相同,否则几个设备都无法注册(我已验证)。如果模块使用该方式注册并且 LED_MAJOR为0(自动分配主设备号 ),使用insmod命令加载模块时会在终端显示分配的主设备号和次设备号,在/dev目录下建立该节点,比如 设备leds,如果加载该模块时分配的主设备号和次设备号为253和0,则建立节点:mknod leds c 253 0。使用register_chrdev (LED_MAJOR,DEVICE_NAME,&dev_fops)注册字符设备驱动程序时都要手动建 立节点 ,否则在应用程序无法打开该设备。


PARTNER CONTENT

文章评论0条评论)

登录后参与讨论
我要评论
0
6
关闭 站长推荐上一条 /3 下一条