嵌入式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灯为开。