linux驅(qū)動(dòng):[3]高級(jí)字符設(shè)備驅(qū)動(dòng)之ioctl
測試平臺(tái): x86 PC linux-4.4.0
1.實(shí)驗(yàn)?zāi)康模?/h2>
- 學(xué)習(xí)并編寫ioctl linux高級(jí)字符設(shè)備驅(qū)動(dòng)程序憎账。
- 編寫驅(qū)動(dòng) scull 录肯,使用5個(gè)指令實(shí)現(xiàn)對(duì)設(shè)備數(shù)據(jù)的清零,讀取,寫入操作溜徙。
2.驅(qū)動(dòng)代碼:(解析見下方)
scull.c:
#include <linux/module.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/cdev.h>
#include <asm/uaccess.h>
#include <linux/device.h>
#include "scull.h"
//設(shè)備私有數(shù)據(jù)
struct scull_dev {
int data;
struct cdev cdev;
} dev;
//最大IOCTL命令號(hào)
#define SCULL_IOC_MAXNR 4
//默認(rèn)自動(dòng)分配主設(shè)備號(hào)
#define SCULL_DEV_MAJOR 0
static int scull_major = SCULL_DEV_MAJOR;
module_param(scull_major, int, S_IRUGO);
struct class *scull_class;
struct cdev cdev;
long scull_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
int err = 0, retval = 0;
//判斷命令幻數(shù)是否匹配
if (_IOC_TYPE(cmd) != SCULL_IOC_MAGIC)
return -ENOTTY;
//判斷命令序號(hào)是否非法
if (_IOC_NR(cmd) > SCULL_IOC_MAXNR)
return -ENOTTY;
//判斷空間是否可訪問
/* VERIFY_WRITE 是 VERIFY_READ 超集 */
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 SCULL_IOC_CLEAR://數(shù)據(jù)清零
dev.data = 0;
printk("SCULL_IOC_CLEAR data: 0\n");
break;
case SCULL_IOC_GET://獲取數(shù)據(jù)(通過指針)
retval = __put_user(dev.data, (int __user *)arg);
printk("SCULL_IOC_GET data: %d\n", dev.data);
break;
case SCULL_IOC_QUERY://獲取數(shù)據(jù)(通過返回值)
printk("SCULL_IOC_QUERY data: %d\n", dev.data);
retval = dev.data;
break;
case SCULL_IOC_SET://設(shè)置數(shù)據(jù)(通過指針)
retval = __get_user(dev.data, (int __user *)arg);
printk("SCULL_IOC_SET data: %d\n", dev.data);
break;
case SCULL_IOC_TELL://設(shè)置數(shù)據(jù)(通過直接引用參數(shù)值)
dev.data = arg;
printk("SCULL_IOC_TELL data: %d\n", arg);
break;
default:
retval = -EINVAL;
break;
}
return retval;
}
static const struct file_operations scull_fops = {
.owner = THIS_MODULE,
.unlocked_ioctl = scull_ioctl,//linux 2.6.36內(nèi)核之后unlocked_ioctl取代ioctl
};
static int scull_init(void)
{
//設(shè)備號(hào)
dev_t devno = MKDEV(scull_major, 0);
int result;
if (scull_major)//靜態(tài)分配設(shè)備號(hào)
result = register_chrdev_region(devno, 1, "scull");
else {//動(dòng)態(tài)分配設(shè)備號(hào)
result = alloc_chrdev_region(&devno, 0, 1, "scull");
scull_major = MAJOR(devno);
}
if (result < 0)
return result;
//用于udev/mdev自動(dòng)創(chuàng)建節(jié)點(diǎn)
scull_class = class_create(THIS_MODULE, "scull");
device_create(scull_class, NULL, devno, NULL, "scull");
//靜態(tài)添加cdev
cdev_init(&cdev, &scull_fops);
cdev.owner = THIS_MODULE;
cdev_add(&cdev, devno, 1);
printk("scull init success\n");
return 0;
}
static void scull_exit(void)
{
cdev_del(&cdev);
device_destroy(scull_class, MKDEV(scull_major, 0));
class_destroy(scull_class);
unregister_chrdev_region(MKDEV(scull_major, 0), 1);
printk("scull exit success\n");
}
MODULE_AUTHOR("Ziping Chen <techping.chan@gmail.com>");
MODULE_LICENSE("GPL");
module_init(scull_init);
module_exit(scull_exit);
scull.h:
#ifndef SCULL_H_
#define SCULL_H_
//定義幻數(shù)
#define SCULL_IOC_MAGIC '$'
//定義命令->
//數(shù)據(jù)清零
#define SCULL_IOC_CLEAR _IO(SCULL_IOC_MAGIC, 0)
//獲取數(shù)據(jù)(通過指針)
#define SCULL_IOC_GET _IOR(SCULL_IOC_MAGIC, 1, int)
//獲取數(shù)據(jù)(通過返回值)
#define SCULL_IOC_QUERY _IO(SCULL_IOC_MAGIC, 2)
//設(shè)置數(shù)據(jù)(通過指針)
#define SCULL_IOC_SET _IOW(SCULL_IOC_MAGIC, 3, int)
//設(shè)置數(shù)據(jù)(通過直接引用參數(shù)值)
#define SCULL_IOC_TELL _IO(SCULL_IOC_MAGIC, 4)
#endif
Makefile:
obj-m := scull.o #編譯進(jìn)模塊
KERNELDIR := /lib/modules/4.4.0-59-generic/build #此處為linux內(nèi)核庫目錄
PWD := $(shell pwd) #獲取當(dāng)前目錄
OUTPUT := $(obj-m) $(obj-m:.o=.ko) $(obj-m:.o=.mod.o) $(obj-m:.o=.mod.c) modules.order Module.symvers
modules:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
clean:
rm -rf $(OUTPUT)
linux c應(yīng)用程序測試:
#include <stdio.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include "scull.h"
int main(void)
{
int fd;
int data;
//打開設(shè)備
fd = open("/dev/scull", O_RDWR);
if (fd == -1) {
printf("open scull device failed!\n");
return -1;
}
//數(shù)據(jù)清零
ioctl(fd, SCULL_IOC_CLEAR);
//直接傳值測試
data = ioctl(fd, SCULL_IOC_QUERY);
printf("app data %d\n", data);
data = 100;
ioctl(fd, SCULL_IOC_TELL, data);
//指針傳值測試
ioctl(fd, SCULL_IOC_GET, &data);
data = 122;
ioctl(fd, SCULL_IOC_SET, &data);
return 0;
}
測試結(jié)果:
result
3.代碼解析:
代碼的大部分解析都位于上面測試,這里我只是提一下程序編寫過程中可能出現(xiàn)的問題:
-
error: unknown field 'ioctl' specified in initializer
這個(gè)錯(cuò)誤是因?yàn)樵趌inux 2.6.36內(nèi)核之后,去掉了原來的ioctl拧篮,添加兩個(gè)新的成員:
long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
將scull_fops中
.ioctl
替換為.unlocked_ioctl
,另外scull_ioctl()要去掉inode參數(shù)牵舱,返回類型為long串绩。 沒有編寫scull_open()函數(shù),設(shè)備默認(rèn)成功打開芜壁。
編譯沒有成功可能是沒有包含對(duì)應(yīng)的頭文件礁凡,頭文件可以通過查閱手冊(cè)或者網(wǎng)絡(luò)搜索得知。
查看printk信息可通過
dmesg
shell指令慧妄。每個(gè)函數(shù)的參數(shù)都是確定的不變的顷牌,不要自己擅自改動(dòng)。
- 我的個(gè)人主頁:http://www.techping.cn/
- 我的個(gè)人站點(diǎn)博客:http://www.techping.cn/blog/wordpress/
- 我的CSDN博客:http://blog.csdn.net/techping
- 我的簡書:http://www.reibang.com/users/b2a36e431d5e/timeline
- 我的GitHub:https://github.com/techping
歡迎相互follow~