驱动步骤:
1、驱动框架:一般读驱动代码需要module_init一层层找代码
2、硬件配置
代码中led_ioctl函数设置引脚的电平高低,该函数是驱动程序对设备的通道进行统一设置/控制的函数
一、 在用户空间,使用ioctl系统调用来控制设备,原型如下:
int ioctl(int fd,unsigned long cmd,...);
fd:文件描述符
cmd:控制命令
...:可选参数:插入*argp,具体内容依赖于cmd
用户程序所作的只是通过命令码告诉驱动程序它想做什么,至于怎么解释这些命令和怎么实现这些命令,这都是驱动程序要做的事情。
二、驱动ioctl方法:
int (*ioctl) (struct inode *inode,struct file *filp,unsigned int cmd,unsigned long arg);
/*
inode与filp两个指针对应于应用程序传递的文件描述符fd,这和传递open方法的参数一样。
cmd 由用户空间直接不经修改的传递给驱动程序
arg 可选。
*/
在驱动程序中实现的ioctl函数体内,实际上是有一个switch {case}结构,每一个case对应一个命令码,做出一些相应的操作。怎么实现这些操作,这是每一个程序员自己的事情,因为设备都是特定的。关键在于怎么样组织命令码,因为在ioctl中命令码是唯一联系用户程序命令和驱动程序支持的途径。
用户使用 int ioctl(int fd,unsinged long cmd,...) 时,...就是要传递的参数;
再通过 int (*ioctl)(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) 中的arg传递;不同颜色代表对应的参数
现阶段能够理解成每一个用户程序的ioctl对应其内核中的一个ioctl函数,且参数需要用户层传递给驱动层 (该例中)
代码很简单 :
#include <linux/module.h> #include <linux/kernel.h> #include <linux/fs.h> #include <linux/init.h> #include <linux/delay.h> #include <asm/uaccess.h> #include <asm/irq.h> #include <asm/io.h> #include <asm/arch/regs-gpio.h> #include <asm/hardware.h> #define DEVICE_NAME "myled" /* 加载模式后,执行”cat /proc/devices”命令看到的设备名称 */ static struct class *leds_class; static struct class *led_dev_class; int major; /* 用来指定LED所用的GPIO引脚 */ static unsigned long led_table[]={ S3C2410_GPF5, S3C2410_GPF6, }; /* 用来指定GPIO引脚的功能:输出 */ static unsigned int led_cfg_table [] = { S3C2410_GPF5_OUTP, S3C2410_GPF6_OUTP, }; /* 应用程序执行ioctl(fd, cmd, arg)时的第2个参数 */ #define LED_ON 0 #define LED_OFF 1 /* 应用程序对设备文件/dev/leds执行open(...)时, * 就会调用s3c24xx_leds_open函数 */ static int led_open (struct inode *inode, struct file *filep) { int i; // 设置GPIO引脚的功能:本驱动中LED所涉及的GPIO引脚设为输出功能 for (i = 0; i <2; i++) { // 设置GPIO引脚的功能:本驱动中LED所涉及的GPIO引脚设为输出功能 s3c2410_gpio_cfgpin(led_table[i], led_cfg_table[i]); } return 0; } static int led_ioctl( struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { if (arg > 2) { return -EINVAL; } switch(cmd) { case LED_ON: // 设置指定引脚的输出电平为0 s3c2410_gpio_setpin(led_table[arg], 0); return 0; case LED_OFF: // 设置指定引脚的输出电平为1 s3c2410_gpio_setpin(led_table[arg], 1); return 0; default: return -EINVAL; } } /* 这个结构是字符设备驱动程序的核心 * 当应用程序操作设备文件时所调用的open、read、write等函数, * 最终会调用这个结构中指定的对应函数 */ static struct file_operations led_ops= { .owner = THIS_MODULE, /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */ .open = led_open, .ioctl = led_ioctl , }; /* * 执行insmod命令时就会调用这个函数 */ static int led_init(void) { int ret; /* 注册字符设备 * 参数为主设备号、设备名字、file_operations结构; * 这样,主设备号就和具体的file_operations结构联系起来了, * 操作主设备为LED_MAJOR的设备文件时,就会调用s3c24xx_leds_fops中的相关成员函数 * LED_MAJOR可以设为0,表示由内核自动分配主设备号 */ major = register_chrdev(0, DEVICE_NAME, &led_ops); if (major < 0) { printk(DEVICE_NAME " can't register major number number::%d\n",major); return ret; } printk(DEVICE_NAME " initialized1\n"); leds_class = class_create(THIS_MODULE, "leds"); if (IS_ERR(leds_class)) return PTR_ERR(leds_class); led_dev_class = class_device_create(leds_class, NULL, MKDEV(major, 0), NULL, "perled"); /* /dev/leds */ return 0; } /* * 执行rmmod命令时就会调用这个函数 */ static void led_exit(void) { class_device_unregister(led_dev_class, MKDEV(major, 0)); class_destroy(leds_class); /* 卸载驱动程序 */ unregister_chrdev(major, DEVICE_NAME); } module_init(led_init); module_exit(led_exit); MODULE_AUTHOR("http://www.100ask.net"); MODULE_DESCRIPTION("S3C2410/S3C2440 LED Driver"); MODULE_LICENSE("GPL");
make
insmod XXX.ko
测试代码(应用程序代码):
#include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <stdio.h> #include <sys/ioctl.h> #include <stdlib.h> /* 应用程序执行ioctl(fd, cmd, arg)时的第2个参数 */ #define IOCTL_LED_ON 0 #define IOCTL_LED_OFF 1 /* * ledtest <dev> <on|off> */ void print_usage(char *file) { printf("Usage:\n"); printf("./a.out 1 on\n"); } int main(int argc,char**argv) { int fd; int ret; int led_NO; char val; if(argc!=3) { printf("error USAGE\n"); exit(1); } //1、打开设备 fd = open("/dev/perled",O_RDWR); led_NO=strtoul(argv[1],0,0)-1;//确定LED if(fd<0) { perror("open fail \n"); return -1; } if (!strcmp("on", argv[2])) { // 亮灯 ioctl(fd,IOCTL_LED_ON,led_NO);// 设备 亮灭 那个led、 } else if (!strcmp("off", argv[2])) { // 灭灯 ioctl(fd,IOCTL_LED_OFF,led_NO); } else { print_usage(argv[0]); return 0; } close(fd); return 0; } /// // // / //./a.out 1 on
驱动程序中的ioctl也可以用write函数但是需要用到copy_from_user
将数据由其用户 空间上传到内核空间了
static ssize_t led_write(struct file *file, const char __user *data, size_t len, loff_t * ppos) { int val; copy_from_user(&val, data, 1); // copy_to_user(); s3c2410_gpio_setpin(S3C2410_GPF5, (val & 0x1));//避免使用if语句 return 1; }
ioctl:http://www.cnblogs.com/geneil/archive/2011/12/04/2275372.html