【Linux Device Driver】—(3.1)—ioctl——程式碼
阿新 • • 發佈:2019-02-01
《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、測試結果