嵌入式LINUX驱动学习之6.混杂设备(一):结构体和函数使用


嵌入式LINUX驱动学习之6.混杂设备(一):结构体和函数使用 )
混杂设备主设备号为10,一般只需要分配次设备号,可以自己指定(如果重复会报错),也可以交给内核分配
使用混杂设备方式编程,混杂设备注册时,自动创建了字符设备,不需要执行mknod命令,具体实现方式见misc_register函数

一、头文件位置及主要结构体主要成员变量说明

头文件位置:include/linux/miscdevice.h"
struct miscdevice  {
    
    
        int minor;  //保存次设备号,一般使用宏MISC_DYNAMIC_MINOR;表示:由内核分配次设备号
        const char *name;   //保存字符设备名称
        const struct file_operations *fops;  //指向文件操作接口对象
       // .....省略更多,在后续分析源码时介绍.......
};

extern int misc_register(struct miscdevice * misc); // 注册混杂设备
extern int misc_deregister(struct miscdevice *misc);//卸载混杂设备

二、内核代码举例:

#include <linux/init.h>
#include <linux/module.h>
#include <linux/gpio.h>
#include <linux/fs.h>
#include <linux/miscdevice.h>
#include <asm/uaccess.h>
#include <cfg_type.h>
struct led_source {
    
    
    char name[10];
    unsigned long gpio ;
};
struct led_source led_info[] = {
    
    
    {
    
    
        .name = "LED1",
        .gpio = PAD_GPIO_C +12
    },
    {
    
    
        .name = "LED2",
        .gpio = PAD_GPIO_C +11
    },
    {
    
    
        .name = "LED3",
        .gpio = PAD_GPIO_C +7
    },
    {
    
    
        .name = "LED4",
        .gpio = PAD_GPIO_B +26
    }
};

void gpio_state(unsigned long kargs[4]){
    
    
    int i =0;
    for(;i < 4 ; i++){
    
    
        kargs[i] = gpio_get_value(led_info[i].gpio);
    }
}
/*用ioctl方式实现开关灯控制,函数说明参考:嵌入式LINUX驱动学习之5*/
#define LED_ON    0X100
#define LED_OFF   0X101
#define LED_STATE 0X110
long myioctl(struct file * file , unsigned int ucmd,unsigned long args){
    
    
    unsigned int kcmd;
    unsigned long kargs[4];
    kcmd = ucmd;
    copy_from_user(&kargs[0],args,sizeof(long));
    switch(kcmd){
    
    
        case LED_ON :
            gpio_set_value(led_info[kargs[0]-1].gpio,0);
            break;
        case LED_OFF :
            gpio_set_value(led_info[kargs[0]-1].gpio,1);
            break;
        case LED_STATE :
            gpio_state(kargs);
            copy_to_user(args,kargs,sizeof(long)*4);
            break;
        default :
            break;
    }
}

struct file_operations fops = {
    
    
    .owner = THIS_MODULE,
    .unlocked_ioctl = myioctl

};

struct miscdevice misc_dev = {
    
     //定义混杂设备结构体对象
    .minor = MISC_DYNAMIC_MINOR,//由内核分配次设备号
    .name = "misc_dev_led",//字符设备名称
    .fops = &fops  //文件操作接口对你象
};

static int led_init(void){
    
    
    int i = 0;
    for(; i <ARRAY_SIZE(led_info);i++){
    
    
        gpio_request(led_info[i].gpio,led_info[i].name);
        gpio_direction_output(led_info[i].gpio,1);
    }
    misc_register(&misc_dev);    //注册混杂设备

    return 0;
}
static void led_exit(void){
    
    
    int i = 0;
    for(; i <ARRAY_SIZE(led_info);i++){
    
    
        gpio_set_value(led_info[i].gpio,1);
        gpio_free(led_info[i].gpio);
    }
    misc_deregister(&misc_dev);//卸载混杂设备

}

module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("GPL");

三、用户空间代码实现:

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#define LED_ON    0X100
#define LED_OFF   0X101
#define LED_STATE 0X110
/*打印LED灯状态信息*/
void led_state_printf(unsigned long uindex[4]){
    
    
    int i =0;
    for(; i<4 ; i++){
    
    
        if(uindex[i] ==0){
    
    
            printf("LED%d灯为开。\n",i+1);
        }
        else {
    
    
            printf("LED%d灯为关。\n",i+1);
        }
    }
}

int main(int argc,char * argv[]){
    
    
    int fp = open(argv[1],O_RDWR);
    unsigned long uindex[4];
    int argv_3;
    if(fp <0){
    
    
        printf ("文件打开失败!!!\n");
        return 0;
    }
    /*
        判断输入胡灯的编号是否正确,只有正确时,才将灯的编号保存到数组中
   */
    if(argc == 4) {
    
    
        argv_3=strtoul(argv[3],NULL,0);
        if((argv_3> 4) || argv_3< 1) {
    
    
            printf("LED灯编号为:1~4\n");
            return -1;
        }
       uindex[0] = argv_3;
    }
    //当用户需要查看LED状态时执行
    if((argc == 3) && (!strcmp(argv[2],"state"))){
    
    
        ioctl(fp,LED_STATE,uindex);
        led_state_printf(uindex);
    }
    //当用户需要打开LED灯时执行
   else if((argc ==4) && (!strcmp(argv[2],"on"))){
    
    
        ioctl(fp,LED_ON,uindex);
    }
    //当用户需要关闭LED灯时执行
    else if((argc ==4) && (!strcmp(argv[2],"off"))){
    
    
        ioctl(fp,LED_OFF,uindex);
    }
    //当用户输入的命令错误时,打印提示信息
    else{
    
    
        printf("命令错误:\n");
        printf("         comm <char_dev_name> <on | off> <led_num>\n");
        printf("         comm <char_dev_name> <state> \n");
        return -1;
    }
    return 0;
}

四、测试

/drivers/test # insmod mymisc_dev.ko 
/drivers/test # ./a  /dev/misc_dev_led on 1
/drivers/test # ./a  /dev/misc_dev_led on 2
/drivers/test # ./a  /dev/misc_dev_led on 4
/drivers/test # ./a  /dev/misc_dev_led state
LED1灯为开。
LED2灯为开。
LED3灯为关。
LED4灯为开。
/drivers/test # ./a  /dev/misc_dev_led off 2
/drivers/test # ./a  /dev/misc_dev_led state
LED1灯为开。
LED2灯为关。
LED3灯为关。
LED4灯为开。

猜你喜欢

转载自blog.csdn.net/weixin_47273317/article/details/107884565