一、字符设备的编程框架
在Linux_kernel驱动开发11中,我们介绍的系统调用。只是为了做一个实验,在真正开发时,我们并不会直接在内核中添加一个新的系统调用,这样做会导致内核体积变大。
1、字符设备结构体
我们实现一个硬件字符设备的驱动程序,实际上是实例化一个struct cdev类型的对象。
【1】struct cdev; // 存储字符设备的相关信息
注意:
在该结构体中,我们只需关注这两个成员,其他的成员由内核自己维护
【2】dev_t dev; // 指示当前的设备号
【3】const struct file_operations *ops; // 操作函数的集合
1)设备号
【1】区分主次设备号
设备号(32bit) = 主设备号(12bit [msb])+ 次设备号(20bit [lsb])
1】示例
ls -l /dev/tty0
主次设备号的范围理论上都是[0, 255]
主设备号:区分不同类型的设备
次设备号:区分同一类型设备的不同个体
MINORBITS:次设备号的位数
MINORMASK:次设备号掩码
MAJOR(dev):得到主设备号
MINOR(dev):得到次设备号
MKDEV(ma,mi):将主设备号与次设备号合为一个32bit整型数(dev_t)
【2】静态注册设备号
就是自己先挑一个没有被内核占用的设备号去注册
0】查看被内核占用的设备号
cat /proc/devices
1】register_chrdev_region(注册设备号)
注释:
from:要注册的起始设备号
count:要连续注册的设备号个数
name:给设备起的名称
2】unregister_chrdev_region(注销设备号)
注释:
from:要注销的起始设备号
count:要连续注销的设备号个数
【3】静态注册实验
1】进入工程目录
cd /home/zjd/s5p6818/KERNEL/drivers
2】创建新的工程
mkdir chrdev
3】编写程序
vim chrdev.c
#include <linux/init.h> #include <linux/module.h> #include <linux/fs.h> #define CHRDEV_MAGOR 200 #define CHRDEV_MINOR 10 #define CHRDEV_NUM 1 #define CHRDEV_NAME "leds" MODULE_LICENSE("GPL"); MODULE_AUTHOR("Zjd"); int dev = 0; // store the major dev number and the minor dev number int __init chrdev_init(void) { int major = CHRDEV_MAGOR; // major dev number int minor = CHRDEV_MINOR; // minor dev number // dev = major << 20 | minor; // there is a define func to do this task dev = MKDEV(major, minor); register_chrdev_region(dev, CHRDEV_NUM, CHRDEV_NAME); //register the number of device return 0; } void __exit chrdev_exit(void) { unregister_chrdev_region(dev, CHRDEV_NUM); return ; } module_init(chrdev_init); module_exit(chrdev_exit);
4】编写Makefile
vim Makefile
obj-m += chrdev.o KERNEL_PATH=/home/zjd/s5p6818/KERNEL/kernel ROOTFS_PATH=/nfs_share/_install all: make -C $(KERNEL_PATH) M=$(PWD) modules cp *.ko $(ROOTFS_PATH) clean: make -C $(KERNEL_PATH) M=$(PWD) clean
5】编译工程
make
6】下位机验证
注意:我们现在只有设备号,而没有设备文件
【4】动态注册设备号
内核自己找一个没有注册的设备号,注册完归程序员使用
1】alloc_chrdev_region(注册设备号)
注释:
dev:回填设备号
baseminor:次设备号的基值(起始值)
count:要连续注册的设备号个数
name:给设备起的名称
2】unregister_chrdev_region(注销设备号)
注释:
from:要注销的起始设备号
count:要连续注销的设备号个数
【5】动态注册实验
1】编写程序
vim chrdev.c
#include <linux/init.h> #include <linux/module.h> #include <linux/fs.h> #define CHRDEV_MAGOR 200 #define CHRDEV_MINOR 10 #define CHRDEV_NUM 1 #define CHRDEV_NAME "leds" MODULE_LICENSE("GPL"); MODULE_AUTHOR("Zjd"); int dev = 0; // store the major dev number and the minor dev number #if 0 // fixed register int __init chrdev_init(void) { int major = CHRDEV_MAGOR; // major dev number int minor = CHRDEV_MINOR; // minor dev number // dev = major << 20 | minor; // there is a define func to do this task dev = MKDEV(major, minor); register_chrdev_region(dev, CHRDEV_NUM, CHRDEV_NAME); //register the number of device return 0; } #else // variable register int __init chrdev_init(void) { int major = CHRDEV_MAGOR; // major dev number int minor = CHRDEV_MINOR; // minor dev number // there is a define func to register the number of devices automatically alloc_chrdev_region(&dev, CHRDEV_MINOR, CHRDEV_NUM, CHRDEV_NAME); major = MAJOR(dev); // gain the major dev number minor = MINOR(dev); // gain the minor dev number printk(KERN_EMERG "dev number is :%d\n major number is :%d\n minor number is :%d\n", dev, major, minor); return 0; } #endif void __exit chrdev_exit(void) { unregister_chrdev_region(dev, CHRDEV_NUM); return ; } module_init(chrdev_init); module_exit(chrdev_exit);
2】编写Makefile
vim Makefile
obj-m += chrdev.o KERNEL_PATH=/home/zjd/s5p6818/KERNEL/kernel ROOTFS_PATH=/nfs_share/_install all: make -C $(KERNEL_PATH) M=$(PWD) modules cp *.ko $(ROOTFS_PATH) clean: make -C $(KERNEL_PATH) M=$(PWD) clean
3】编译工程
make
4】下位机验证
注意:我们现在只有设备号,而没有设备文件
2)操作函数的集合
【1】操作函数集合
const struct file_operations *ops; // 操作函数的集合
实现一个字符设备驱动程序的主要编程工作都集中在操作函数集合,我们将来具体到某一个字符设备驱动程序的时候,只需要实现下列函数集合的子集就可以了。
【2】内核中提供的操作cdev的API
1】cdev_init(初始化cdev结构体)
2】cdev_add(将cdev注册到内核)
注释:
p:要注册的cdev地址
dev:要注册的设备号
count:要连续注册的cdev个数
3】cdev_del(从内核中注销cdev)
注释:
p:要注销的cdev地址
【3】实验
1】进入工程目录
cd /home/zjd/s5p6818/KERNEL/drivers
2】创建新的工程
mkdir chrdev_func
3】编写程序
vim chrdev_func.c
#include <linux/init.h> #include <linux/module.h> #include <linux/fs.h> #include <linux/cdev.h> #define CHRDEV_MAGOR 200 #define CHRDEV_MINOR 10 #define CHRDEV_NUM 1 #define CHRDEV_NAME "myleds" MODULE_LICENSE("GPL"); MODULE_AUTHOR("Zjd"); int dev = 0; // store the major dev number and the minor dev number // 1_step :define a struct cdev be named led_cdev struct cdev led_cdev; // 3_step :implement the function of led_fops int led_open(struct inode *inode, struct file *fp) { printk(KERN_EMERG "enter:%s\n", __FUNCTION__); return 0; } int led_close(struct inode *inode, struct file *fp) { printk(KERN_EMERG "enter:%s\n", __FUNCTION__); return 0; } // 2_step :define a struct file_operation be named led_fops // what functions shall we to implement ? // there is turn_on and turn_off of the leds // So, we should to implement the function of open() and release(), eithor or we should keep up the same type as the definations of the struct file_operations struct file_operations led_fops = { .owner = THIS_MODULE, // int (*open) (struct inode *, struct file *); .open = led_open, // int (*release) (struct inode *, struct file *); .release = led_close }; #if 0 // fixed register int __init chrdev_init(void) { int major = CHRDEV_MAGOR; // major dev number int minor = CHRDEV_MINOR; // minor dev number // dev = major << 20 | minor; // there is a define func to do this task dev = MKDEV(major, minor); register_chrdev_region(dev, CHRDEV_NUM, CHRDEV_NAME); //register the number of device return 0; } #else // variable register int __init chrdev_init(void) { int major = CHRDEV_MAGOR; // major dev number int minor = CHRDEV_MINOR; // minor dev number // there is a define func to register the number of devices automatically alloc_chrdev_region(&dev, CHRDEV_MINOR, CHRDEV_NUM, CHRDEV_NAME); major = MAJOR(dev); // gain the major dev number minor = MINOR(dev); // gain the minor dev number printk(KERN_EMERG "dev number is :%d\n major number is :%d\n minor number is :%d\n", dev, major, minor); // 4_step :initalize the struct cdev object led_cdev cdev_init(&led_cdev, &led_fops); // 5_step :register led_cdev into Kernel cdev_add(&led_cdev, dev, CHRDEV_NUM); return 0; } #endif void __exit chrdev_exit(void) { // 6_step :destory cdev cdev_del(&led_cdev); unregister_chrdev_region(dev, CHRDEV_NUM); return ; } module_init(chrdev_init); module_exit(chrdev_exit);
4】编写Makefile
vim Makefile
obj-m += chrdev_func.o KERNEL_PATH=/home/zjd/s5p6818/KERNEL/kernel ROOTFS_PATH=/nfs_share/_install all: make -C $(KERNEL_PATH) M=$(PWD) modules cp *.ko $(ROOTFS_PATH) clean: make -C $(KERNEL_PATH) M=$(PWD) clean
5】编译工程
make
6】下位机安装模块
7】写一个应用层程序测试
mkdir test
cd test
vim led_test.c
#include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #define CDEV_PATH "/dev/myleds" int main(void) { int fd = 0; if((fd = open(CDEV_PATH, O_RDWR)) < 0) { perror("open()"); return -1; } printf("open success!\n"); sleep(5); printf("closing...\n"); close(fd); return 0; }
arm-cortex_a9-linux-gnueabi-gcc led_test.c -o led_test
cp led_test /nfs_share/_install/
8】下位机测试
手动创建设备文件
mknod /dev/myleds c 244 10
./led_test
9】疑惑
内核中的打印函数与应用程序中的打印函数,执行顺序孰先孰后?
不确定:内核打印输出的是自己维护的缓冲区,应用程序打印输出的是标准输出缓冲区
二、GPIO库
1、读懂开发板原理图
LED0 GPIOB26
LED1 GPIOC11
LED2 GPIOC7
LED3 GPIOC12
输出低电平,灯亮
输出高电平,灯灭
2、CPU Data Sheet
#define GPIOBOUT *(volatile unsigned int *)0xC001B000
#define GPIOBOUT *(volatile unsigned int *)0xC001B000
#define GPIOBOUT *(volatile unsigned int *)0xC001B000
#define GPIOBOUT *(volatile unsigned int *)0xC001B000
#define GPIOBOUT *(volatile unsigned int *)0xC001B000
#define GPIOBOUT *(volatile unsigned int *)0xC001B000
3、内核中提供的操作GPIO的API
【0】gpio宏定义
【1】gpio_request(申请GPIO管脚)
int gpio_request(unsigned gpio, const char *label)
【2】使用GPI管脚
1】gpio_direction_input(设置输入)
2】gpio_direction_output(设置输出)
3】gpio_set_value(设置value)
4】gpio_get_value(获取value)
【3】gpio_free(释放GPIO管脚)
void gpio_free(unsigned gpio)
4、实验
【1】进入工程目录
cd /home/zjd/s5p6818/KERNEL/drivers
【2】创建新的工程
mkdir led_drv
【3】编写程序
vim led_drv.c
#include <linux/init.h> #include <linux/module.h> #include <linux/fs.h> #include <linux/cdev.h> #include <linux/gpio.h> #include <mach/platform.h> #define CHRDEV_MAGOR 200 #define CHRDEV_MINOR 26 #define CHRDEV_NUM 1 #define CHRDEV_NAME "myleds" #define HIGH 1 #define LOW 0 MODULE_LICENSE("GPL"); MODULE_AUTHOR("Zjd"); int dev = 0; // store the major dev number and the minor dev number // 1_step :define a struct cdev be named led_cdev struct cdev led_cdev; // 3_step :implement the function of led_fops int led_open(struct inode *inode, struct file *fp) { printk(KERN_EMERG "enter:%s\n", __FUNCTION__); // c_step :set the value=0(turn on) of the gpio gpio_set_value(PAD_GPIOB26, LOW); return 0; } int led_close(struct inode *inode, struct file *fp) { printk(KERN_EMERG "enter:%s\n", __FUNCTION__); // c_step :set the value=1(turn off) of the gpio gpio_set_value(PAD_GPIOB26, HIGH); return 0; } // 2_step :define a struct file_operation be named led_fops // what functions shall we to implement ? // there is turn_on and turn_off of the leds // So, we should to implement the function of open() and release(), eithor or we should keep up the same type as the definations of the struct file_operations struct file_operations led_fops = { .owner = THIS_MODULE, // int (*open) (struct inode *, struct file *); .open = led_open, // int (*release) (struct inode *, struct file *); .release = led_close }; #if 0 // fixed register int __init chrdev_init(void) { int major = CHRDEV_MAGOR; // major dev number int minor = CHRDEV_MINOR; // minor dev number // dev = major << 20 | minor; // there is a define func to do this task dev = MKDEV(major, minor); register_chrdev_region(dev, CHRDEV_NUM, CHRDEV_NAME); //register the number of device return 0; } #else // variable register int __init chrdev_init(void) { int major = CHRDEV_MAGOR; // major dev number int minor = CHRDEV_MINOR; // minor dev number // there is a define func to register the number of devices automatically alloc_chrdev_region(&dev, CHRDEV_MINOR, CHRDEV_NUM, CHRDEV_NAME); major = MAJOR(dev); // gain the major dev number minor = MINOR(dev); // gain the minor dev number printk(KERN_EMERG "dev number is :%d\n major number is :%d\n minor number is :%d\n", dev, major, minor); // 4_step :initalize the struct cdev object led_cdev cdev_init(&led_cdev, &led_fops); // 5_step :register led_cdev into Kernel cdev_add(&led_cdev, dev, CHRDEV_NUM); // a_step :apply gpio gpio_request(PAD_GPIOB26, "LED0"); // b_step :set the default value=1(turn_off) of GPIOB26 gpio_direction_output(PAD_GPIOB26, HIGH); return 0; } #endif void __exit chrdev_exit(void) { // e_step :release gpio gpio_free(PAD_GPIOB26); // 6_step :destory cdev cdev_del(&led_cdev); unregister_chrdev_region(dev, CHRDEV_NUM); // unregister the number of dev return ; } module_init(chrdev_init); module_exit(chrdev_exit);
【4】编写Makefile
vim Makefile
obj-m += led_drv.o KERNEL_PATH=/home/zjd/s5p6818/KERNEL/kernel ROOTFS_PATH=/nfs_share/_install all: make -C $(KERNEL_PATH) M=$(PWD) modules cp *.ko $(ROOTFS_PATH) clean: make -C $(KERNEL_PATH) M=$(PWD) clean
【5】编译工程
make
【6】下位机安装模块
【7】编写应用层程序
mkdir test
cd test
vim led_test.c
#include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #define CDEV_PATH "/dev/myleds" int main(void) { int fd = 0; if((fd = open(CDEV_PATH, ORDWR)) < 0) { perror("open()"); return -1; } printf("open success!\n"); sleep(5); printf("closing...\n"); close(fd); return 0; }
arm-cortex_a9-linux-gnueabi-gcc led_test.c -o led_test
cp led_test /nfs_share/_install/
【8】下位机测试
手动创建设备文件
mknod /dev/myleds c 244 26
./led_test
三、用户态与内核态的数据交互
用户空间不能直接访问内核空间
内核空间不能直接访问用户空间
1)内核中提供的数据交互的API
【1】传递多数据
1】copy_to_user(内核到用户)
int copy_to_user(void __user *to, const void *from, int n)
注释:
to:内核空间缓冲区地址,
from:用户空间地址
n:数据字节数
retval:不能被复制的字节数,返回0表示全部复制成功。
2】copy_from_user(用户到内核)
int copy_from_user(void *to, const void __user *from, int n)
注释:
to:内核空间缓冲区地址,
from:用户空间地址
n:数据字节数
retval:不能被复制的字节数,返回0表示全部复制成功。
【2】传递单数据
可以从指定空间获取单个数据,单个数据并不是指一个字节数据,对ARM而言,一次性可获取一个char、short或者 int型的数据,即1、2或者4字节。
1】put_user(x, ptr)(内核到用户)
注释:
x :内核空间的数据,
p :用户空间的指针。
传递成功,返回 0,否则返回-EFAULT。
2】get_user(x, ptr)(用户到内核)
注释:
x :内核空间的数据,
p :用户空间的指针。
传递成功,返回 0,否则返回-EFAULT。
注意:
以上API与C标准库中memcpy(3)相似,但多了一个对访问的空间的权限检查
2、实验
【1】进入工程目录
cd /home/zjd/s5p6818/KERNEL/drivers
【2】创建新的工程
mkdir param_drv
【3】编写程序
vim param_drv.c
#include <linux/init.h> #include <linux/module.h> #include <linux/fs.h> #include <linux/cdev.h> #include <linux/gpio.h> #include <mach/platform.h> #include <linux/uaccess.h> #define CHRDEV_MAGOR 200 #define CHRDEV_MINOR 26 #define CHRDEV_NUM 1 #define CHRDEV_NAME "myleds" #define HIGH 1 #define LOW 0 #define LED0 (PAD_GPIO_B + 26) #define LED1 (PAD_GPIO_C + 12) #define LED2 (PAD_GPIO_C + 7) #define LED3 (PAD_GPIO_C + 11) unsigned int leds[] = {LED0, LED1, LED2, LED3}; const char *leds_label[] = {"LED0", "LED1", "LED2", "LED3"}; MODULE_LICENSE("GPL"); MODULE_AUTHOR("Zjd"); int dev = 0; struct cdev led_cdev; int k_cmd = 0; //kernel's buffer int k_status = 0; //the state of LEDs int led_open(struct inode *inode, struct file *fp) { return 0; } int led_close(struct inode *indoe, struct file *fp) { return 0; } ssize_t led_read(struct file *fp, char __user *buf, size_t len, loff_t *offset) { int ret = 0; ret = copy_to_user(buf, &k_status, len); return len; } ssize_t led_write(struct file *fp, const char __user *buf, size_t len, loff_t *offset) { int ret = 0; int i = 0; ret = copy_from_user(&k_cmd, buf, len); for (i = 0; i < sizeof(leds) / sizeof(leds[0]); i++) { gpio_set_value(leds[i], k_cmd); } k_status = k_cmd; return len; } struct file_operations led_fops = { .owner = THIS_MODULE, .open = led_open, .release = led_close, .read = led_read, .write = led_write }; int __init chrdev_init(void) { int major = CHRDEV_MAGOR; int minor = CHRDEV_MINOR; int i = 0; alloc_chrdev_region(&dev, CHRDEV_MINOR, CHRDEV_NUM, CHRDEV_NAME); major = MAJOR(dev); minor = MINOR(dev); printk(KERN_EMERG "major = %d\nminor = %d\n", major, minor); cdev_init(&led_cdev, &led_fops); cdev_add(&led_cdev, dev, CHRDEV_NUM); for (i = 0; i < sizeof(leds) / sizeof(leds[0]); i++) { gpio_request(leds[i], leds_label[i]); gpio_direction_output(leds[i], HIGH); } return 0; } void __exit chrdev_exit(void) { int i = 0; for (i = 0; i < sizeof(leds) / sizeof(leds[0]); i++) { gpio_free(LED1); } cdev_del(&led_cdev); unregister_chrdev_region(dev, CHRDEV_NUM); return ; } module_init(chrdev_init); module_exit(chrdev_exit);
【4】编写Makefile
vim Makefile
obj-m += param_drv.o KERNEL_PATH=/home/zjd/s5p6818/KERNEL/kernel ROOTFS_PATH=/nfs_share/_install all: make -C $(KERNEL_PATH) M=$(PWD) modules cp *.ko $(ROOTFS_PATH) clean: make -C $(KERNEL_PATH) M=$(PWD) clean
【5】编译工程
make
【6】下位机安装模块
【7】编写应用层程序
mkdir test
cd test
vim led_test.c
#include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #include <string.h> #define ON 0 #define OFF 1 #define CDEV_PATH "/dev/myleds" int main(int argc, char *argv[]) { int fd = 0; int cmd = 0; int status = 0; if (argc < 2) { printf("Usage : %s <on/off>\n", argv[0]); return -1; } if (!strcmp(argv[1], "on")) { cmd = ON; } else if (!strcmp(argv[1], "off")){ cmd = OFF; } else { printf("illegal param\n"); return -2; } if((fd = open(CDEV_PATH, O_RDWR)) < 0) { perror("open()"); return -3; } printf("open success!\n"); write(fd, &cmd, sizeof(cmd)); read(fd, &status, sizeof(status)); if (status == ON) { printf("Led is On!\n"); } else { printf("Led is Off!\n"); } printf("closing...\n"); close(fd); return 0; }
vim Makefile
SRC=led_test.c OBJ=led_test ARM_COMPILE=arm-cortex_a9-linux-gnueabi- GCC=gcc ROOTFS_PATH=/nfs_share/_install all: $(ARM_COMPILE)$(GCC) $(SRC) -o $(OBJ) cp $(OBJ) $(ROOTFS_PATH) clean: rm -rf $(OBJ)
【8】编译工程
make
【9】下位机测试
手动创建设备文件
mknod /dev/myleds c 244 26
./led_test
四、ioctl
1)介绍
2)实操
【1】进入工程目录
cd /home/zjd/s5p6818/KERNEL/drivers
【2】创建新的工程
mkdir ioctl
【3】编写程序
vim ioctl.c
#include <linux/init.h> #include <linux/module.h> #include <linux/fs.h> #include <linux/cdev.h> #include <linux/gpio.h> #include <mach/platform.h> #include <linux/uaccess.h> MODULE_LICENSE("GPL"); MODULE_AUTHOR("Zjd"); #define CHRDEV_MAGOR 200 #define CHRDEV_MINOR 26 #define CHRDEV_NUM 1 #define CHRDEV_NAME "myleds" #define HIGH 1 #define LOW 0 #define LED0 (PAD_GPIO_B + 26) #define LED1 (PAD_GPIO_C + 12) #define LED2 (PAD_GPIO_C + 7) #define LED3 (PAD_GPIO_C + 11) #define TURN_ON LOW #define TURN_OFF HIGH dev_t dev = 0; struct cdev led_cdev; typedef struct led_desc{ unsigned int gpio; char *name; }led_desc_t; led_desc_t leds[] = { {LED0, "LED0"}, {LED1, "LED1"}, {LED2, "LED2"}, {LED3, "LED3"} }; long led_ioctl(struct file *fp, unsigned int cmd, unsigned long arg) { int k_index = 0; int ret = 0; ret = copy_from_user(&k_index, (const void *)arg, sizeof(int)); if (k_index > 4 || k_index < 1) return -EINVAL; switch (cmd) { case TURN_ON: gpio_set_value(leds[k_index - 1].gpio, LOW); break; case TURN_OFF: gpio_set_value(leds[k_index - 1].gpio, HIGH); break; default: return -EINVAL; } return arg; } struct file_operations led_fops = { .owner = THIS_MODULE, .unlocked_ioctl = led_ioctl }; int __init chrdev_init(void) { int major = CHRDEV_MAGOR; int minor = CHRDEV_MINOR; int i = 0; alloc_chrdev_region(&dev, CHRDEV_MINOR, CHRDEV_NUM, CHRDEV_NAME); major = MAJOR(dev); minor = MINOR(dev); printk(KERN_EMERG "major = %d\nminor = %d\n", major, minor); cdev_init(&led_cdev, &led_fops); cdev_add(&led_cdev, dev, CHRDEV_NUM); for (i = 0; i < ARRAY_SIZE(leds); i++) { gpio_request(leds[i].gpio, leds[i].name); gpio_direction_output(leds[i].gpio, HIGH); } return 0; } void __exit chrdev_exit(void) { int i = 0; for (i = 0; i < ARRAY_SIZE(leds); i++) { gpio_free(leds[i].gpio); } cdev_del(&led_cdev); unregister_chrdev_region(dev, CHRDEV_NUM); return ; } module_init(chrdev_init); module_exit(chrdev_exit);
【4】编写Makefile
vim Makefile
obj-m += ioctl.o KERNEL_PATH=/home/zjd/s5p6818/KERNEL/kernel ROOTFS_PATH=/nfs_share/_install all: make -C $(KERNEL_PATH) M=$(PWD) modules cp *.ko $(ROOTFS_PATH) clean: make -C $(KERNEL_PATH) M=$(PWD) clean
【5】编译工程
make
【6】下位机安装模块
【7】编写应用层程序
mkdir test
cd test
vim led_test.c
#include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #include <string.h> #include <sys/ioctl.h> #include <stdlib.h> #define ON 0 #define OFF 1 #define CDEV_PATH "/dev/myleds" int main(int argc, char *argv[]) { int fd = 0; int cmd = 0; int index = 0; if (argc < 3) { printf("Usage : %s <on/off> <1/2/3/4>\n", argv[0]); return -1; } if (!strcmp(argv[1], "on")) { cmd = ON; } else if (!strcmp(argv[1], "off")){ cmd = OFF; } else { printf("illegal param\n"); return -2; } index = atoi(argv[2]); if (index < 1 || index > 4) { printf("illegal param\n"); return -2; } if((fd = open(CDEV_PATH, O_RDWR)) < 0) { perror("open()"); return -3; } printf("open success!\n"); ioctl(fd, cmd, &index); printf("closing...\n"); close(fd); return 0; }
vim Makefile
SRC=led_test.c OBJ=led_test ARM_COMPILE=arm-cortex_a9-linux-gnueabi- GCC=gcc ROOTFS_PATH=/nfs_share/_install all: $(ARM_COMPILE)$(GCC) $(SRC) -o $(OBJ) cp $(OBJ) $(ROOTFS_PATH) clean: rm -rf $(OBJ)
【8】编译工程
make
【9】下位机测试
手动创建设备文件
mknod /dev/myleds c 244 26
./led_test