《Linux Device Driver》这本书的却做的很好,对于一个初学者来说虽然有点难度,但是只要认真看,绝对是大有裨益的!
好了,昨天把ioctl的原理以及涉及到的代码贴了一下,今天就做了做实验,感觉还凑合,所以就贴出来!对自己也算是做个笔记吧!
今天这个实验主要就是通过ioctl来控制LED灯的亮灭,虽然有点简单,但是毕竟也是需要花费点时间的。
1、驱动程序
①、tiny6410_led_ioctl.c
#include <linux/module.h>
#include <linux/types.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/mm.h>
#include <linux/sched.h>
#include <linux/init.h>
#include <linux/cdev.h>
#include <linux/slab.h>
#include <asm/io.h>
#include <asm/system.h>
#include <asm/uaccess.h>
#include <linux/ioport.h>
#include <asm/io.h>
#include <linux/miscdevice.h>
#include <linux/device.h>
#include <linux/cdev.h>
#include <linux/ioctl.h>
#include "tiny6410_led_ioctl.h"
MODULE_LICENSE("GPL");
static struct cdev cdev;
static dev_t devno;
struct class *tiny6410_class;
static int major;
volatile unsigned long *gpkcon0 = NULL;
volatile unsigned long *gpkcon1 = NULL;
volatile unsigned long *gpkdat = NULL;
int led_open(struct inode *inode, struct file *filp)
{
/* LED1 - 4 分别对应GPK4 -7 */
/* 设置LED 引脚为输出引脚 */
*gpkcon0 &= (((0x1) << (4*4)) | ((0x1) << (5*4)) | ((0x1) << (6*4)) | ((0x1) << (7*4)));
/* 对应管脚置高,使LED 全灭 */
*gpkdat |= 0xf0;
return 0;
}
static ssize_t led_write(struct file *filp, char __user *buf, size_t size, loff_t *ppos)
{
/* LED 全部熄灭或者全部打开 */
int val;
copy_from_user(&val, buf, size);
/* 点灯 */
if(val == 1) {
*gpkdat &= ~((1<<4) | (1<<5) | (1<<6) | (1<<7));
}
/* 灭灯 */
else {
*gpkdat |= ((1<<4) | (1<<5) | (1<<6) | (1<<7));
}
}
static unsigned long led_getdat(void)
{
return ((*gpkdat >> 4)&0x0f);
}
static void led_setdat(int dat)
{
*gpkdat = (*gpkdat & ~(0xf<<4)) |((dat&0xf) << 4);
}
int led_ioctl(struct file *filp,unsigned int cmd,unsigned long arg)
{
int err = 0;
int ret = 0;
int ioarg = 0;
/* 检测命令的有效性 */
if(_IOC_TYPE(cmd) != LED_IOCTL_MAGIC)
return -EINVAL;
if(_IOC_NR(cmd) >= LED_IOCTL_MAXNR)
return -EINVAL;
/* 根据命令类型,检测空间是否可以访问,当然有的函数可以省去此步骤。如copy_to_user,下面的就是应用到了,这里为了以后参照,所以也写上了 */
if(_IOC_DIR(cmd) & _IOC_READ)
err = !access_ok(VERIFY_WRITE, (void *)arg, _IOC_SIZE(cmd));
else if(_IOC_DIR(cmd) & _IOC_WRITE)
err = !access_ok(VERIFY_READ, (void *)arg, _IOC_SIZE(cmd));
if(err)
return -EFAULT;
/* 根据命令执行相应的操作 */
switch(cmd) {
/* 打印当前信息 */
case LED_IOCTL_IOPRINT:
printk("<---CMD LED_IOCTL DONE--->\n");
break;
/* 获取当前LED 灯的显值 */
case LED_IOCTL_IOGETDAT:
ioarg = led_getdat();
ret = put_user(ioarg, (int *)arg);
printk("[Call LED_IOCTL_IOGETDAT!]");
break;
/* 设置当前LED 灯 */
case LED_IOCTL_IOSETDAT:
ret = get_user(ioarg, (int *)arg);
led_setdat(ioarg);
break;
default:
return -EINVAL;
}
return ret;
}
static const struct file_operations led_fops =
{
.open = led_open,
.write = led_write,
.owner = THIS_MODULE,
.unlocked_ioctl = led_ioctl,
};
static void tiny6410_led_pin_setup(void)
{
gpkcon0 = (volatile unsigned long *)ioremap(0x7F008800, 16);
gpkcon1 = gpkcon0 + 1;
gpkdat = gpkcon0 + 2;
}
static void tiny6410_led_pin_release(void)
{
iounmap(gpkcon0);
iounmap(gpkcon1);
iounmap(gpkdat);
}
static int __init led_init(void)
{
/*
alloc_chrdev_region(&devno, 0, 1, "tiny6410_led");
major = MAJOR(devno); */
major = 255;
devno = MKDEV(major, 0);
register_chrdev_region(devno, 1, "tiny6410_led_ioctl");
/* 初始化cdev 结构 */
cdev_init(&cdev,&led_fops);
cdev.owner = THIS_MODULE;
/* 注册字符设备 */
cdev_add(&cdev, devno,1);
tiny6410_class = class_create(THIS_MODULE, "led_class");
device_create(tiny6410_class, NULL, MKDEV(major, 0), NULL, "tiny6410_led_ioctl");
tiny6410_led_pin_setup();
return 0;
}
static void __exit led_exit(void)
{
tiny6410_led_pin_release();
device_destroy(tiny6410_class, MKDEV(major, 0));
class_destroy(tiny6410_class);
cdev_del(&cdev);
unregister_chrdev_region(devno, 1);
}
module_init(led_init);
module_exit(led_exit);
②、tiny6410_led_ioctl.h
#ifndef _LED_H_
#define _LED_H_
#include <linux/ioctl.h>
/* 定义幻数 */
#define LED_IOCTL_MAGIC 'k'
/* 定义命令 */
#define LED_IOCTL_IOPRINT _IO(LED_IOCTL_MAGIC, 1)
#define LED_IOCTL_IOGETDAT _IOR(LED_IOCTL_MAGIC, 2, int)
#define LED_IOCTL_IOSETDAT _IOW(LED_IOCTL_MAGIC, 3, int)
/* 命令总数 */
#define LED_IOCTL_MAXNR 3
#endif
2、测试程序
①、tiny6410_led_ioctl_app.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "tiny6410_led_ioctl.h"
#define DEVICE_NAME "/dev/tiny6410_led_ioctl"
int binstr_to_int(char *binstr)
{
int ret = 0;
int i = 0;
char bnum[5];
memset(bnum,'0',4);
int len = strlen(binstr);
if(len > 4)
strcpy(bnum,binstr + len - 4);
else
strcpy(bnum + 4 - len,binstr);
for(i = 0;i < 4;i ++) {
ret <<= 1;
ret += (bnum[i] == '0' ? 1 : 0);
}
return ret;
}
int main(int argc,char **argv)
{
if(argc > 2) {
printf("Usage: %s <binary code>\n"
"example: %s 1001 -- Will turn on led 0 and 3, and turn off led 1 and 2.\n",argv[0],argv[0]);
_exit(EXIT_FAILURE);
}
int fd,arg;
if((fd = open(DEVICE_NAME,O_RDWR)) == -1) {
printf("Open dev error!\n");
_exit(EXIT_FAILURE);
}
if(argc == 1) {
ioctl(fd,LED_IOCTL_IOGETDAT,&arg);
printf("led dat: %d.\n",arg);
}
else {
arg = binstr_to_int(argv[1]);
printf("arg = %d.\n",arg);
ioctl(fd,LED_IOCTL_IOSETDAT,&arg);
}
_exit(EXIT_SUCCESS);
}
3、测试结果