1. 程式人生 > >【Linux Device Driver】—(3.1)—ioctl——程式碼

【Linux Device Driver】—(3.1)—ioctl——程式碼

《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、測試結果