1、流程
2、LED灯驱动程序(基于设备树)
/*************************************************************************
> File Name: led_dtb.c
> Author: wrf
> Mail: wrf6758@qq.com
> Created Time: 2022年09月13日 星期二 14时48分44秒
> led灯设备驱动程序设备树版
************************************************************************/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/wait.h>
#include <linux/sched.h>
#include <linux/poll.h>
#include <linux/slab.h>
#include <linux/mm.h>
#include <linux/io.h>
#include <asm/uaccess.h>
#include <asm/atomic.h>
#include <linux/gpio.h>
#include <linux/of_gpio.h>
#include "led_dev.h"
int major = 12;
int minor = 0;
int myled_num = 1;
struct myled_dev
{
struct cdev mydev;
unsigned int led2gpio;
unsigned int led3gpio;
unsigned int led4gpio;
unsigned int led5gpio;
};
struct myled_dev *pleddev = NULL;
int led_open(struct inode *pnode,struct file *pfile)
{
pfile->private_data = (void *)(container_of(pnode->i_cdev, struct myled_dev, mydev));
return 0;
}
int led_close(struct inode *pnode,struct file *pfile)
{
return 0;
}
void led_on(struct myled_dev *pleddev, int whitch_led)
{
switch(whitch_led)
{
case 2:
gpio_set_value(pleddev->led2gpio, 1);
break;
case 3:
gpio_set_value(pleddev->led3gpio, 1);
break;
case 4:
gpio_set_value(pleddev->led4gpio, 1);
break;
case 5:
gpio_set_value(pleddev->led5gpio, 1);
break;
}
}
void led_off(struct myled_dev *pleddev, int whitch_led)
{
switch(whitch_led)
{
case 2:
gpio_set_value(pleddev->led2gpio, 0);
break;
case 3:
gpio_set_value(pleddev->led3gpio, 0);
break;
case 4:
gpio_set_value(pleddev->led4gpio, 0);
break;
case 5:
gpio_set_value(pleddev->led5gpio, 0);
break;
}
}
long led_ioctl(struct file *pfile,unsigned int cmd,unsigned long arg)
{
struct myled_dev *pleddev = (struct myled_dev *)pfile->private_data;
/*led2-led5*/
if(arg < 2 || arg >5)
{
return -1;
}
switch(cmd)
{
case MY_LED_ON:
led_on(pleddev, arg);
break;
case MY_LED_OFF:
led_off(pleddev, arg);
break;
default:
return -1;
}
return 0;
}
struct file_operations myops = {
.owner = THIS_MODULE,
.open = led_open,
.release = led_close,
.unlocked_ioctl = led_ioctl,
};
/*向内核申请GPIO*/
void request_leds_gpio(struct myled_dev *pleddev, struct device_node *pnode)
{
/**
* include/of_gpio.h
* of_get_named_gpio - 从设备树中提取gpio口
* @np - 设备节点指针
* @propname - 属性名
* @index - gpio口引脚标号
* 成功:得到GPIO口编号;失败:负数,绝对值是错误码
*/
pleddev->led2gpio = of_get_named_gpio(pnode, "led2-gpio", 0);
/*其实就是让内核检查一下该GPIO引脚是否被其它设备占用,如果没有占用则返回0并用label做一下标记,表示被本设备占用,否则返回负数*/
gpio_request(pleddev->led2gpio, "led2");
pleddev->led3gpio = of_get_named_gpio(pnode, "led3-gpio", 0);
gpio_request(pleddev->led3gpio, "led3");
pleddev->led4gpio = of_get_named_gpio(pnode, "led4-gpio", 0);
gpio_request(pleddev->led4gpio, "led4");
pleddev->led5gpio = of_get_named_gpio(pnode, "led5-gpio", 0);
gpio_request(pleddev->led5gpio, "led5");
}
/*配置相应GPIO为输出模式*/
void set_led_gpio_output(struct myled_dev *pleddev)
{
/*在GPIO口写上0的同时,把端口设置为输出模式*/
gpio_direction_output(pleddev->led2gpio, 0);
gpio_direction_output(pleddev->led3gpio, 0);
gpio_direction_output(pleddev->led4gpio, 0);
gpio_direction_output(pleddev->led5gpio, 0);
}
/*向内核归还对该GPIO引脚的使用权*/
void free_leds_gpio(struct myled_dev *pleddev)
{
gpio_free(pleddev->led2gpio);
gpio_free(pleddev->led3gpio);
gpio_free(pleddev->led4gpio);
gpio_free(pleddev->led5gpio);
}
int __init led_init(void)
{
int ret = 0;
dev_t devno = MKDEV(major, minor);
struct device_node *pnode = NULL; //对应设备树中的一个节点
/*通过路径查找指定节点*/
pnode = of_find_node_by_path("/fs4412-leds");
if(NULL == pnode)
{
printk("fine node failed\n");
return -1;
}
/*申请设备号*/
ret = register_chrdev_region(devno,myled_num,"myled");
if(ret)
{
ret = alloc_chrdev_region(&devno,minor,myled_num,"myled");
if(ret)
{
printk("get devno failed\n");
return -1;
}
//更新主设备号
major = MAJOR(devno);//容易遗漏,注意
}
/*动态申请内存空间*/
//GFP_KERNEL —— 正常分配内存;
pleddev = (struct myled_dev*)kmalloc(sizeof(struct myled_dev),GFP_KERNEL);
if(NULL == pleddev)
{
//申请失败,记得释放设备号
unregister_chrdev_region(devno,myled_num);
printk("kmallc for struct myled_dev failed\n");
return -1;
}
memset(pleddev, 0, sizeof(struct myled_dev));
/*给struct cdev指定操作对像*/
cdev_init(&pleddev->mydev, &myops);
/*将struct cdev添加到内核对应的数据结构里*/
pleddev->mydev.owner = THIS_MODULE;
cdev_add(&pleddev->mydev, devno, myled_num);
/*向内核申请GPIO*/
request_leds_gpio(pleddev, pnode);
/*将寄存器设置为输出,led初始为灭*/
set_led_gpio_output(pleddev);
return 0;
}
void __exit led_exit(void)
{
dev_t devno = MKDEV(major, minor);
/*归还节点给内核*/
free_leds_gpio(pleddev);
cdev_del(&pleddev->mydev);
unregister_chrdev_region(devno, myled_num);
kfree(pleddev);
pleddev = NULL;
}
MODULE_LICENSE("GPL");
module_init(led_init);
module_exit(led_exit);
3、CGI程序
#include <stdio.h>
#include "cgic.h"
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <sys/ioctl.h>
#define mytype 'g'
typedef struct led_desc{
int led_num; //2 3 4 5
int led_state; //0 or 1
}led_desc_t;
#define FS_LED_ON _IO(mytype,1)
#define FS_LED_OFF _IO(mytype,0)
#define LED "/dev/led_test"
int main(int argc, char const *argv[])
{
int i = 0,j = 3;
int nread;
int led_control,led_state;
int led_fd,fifo_fd;
led_desc_t led;
char *data;
led_fd = open(LED,O_RDWR);
if(led_fd < 0){
printf("open failed !\n");
}
printf("open device success! led_fd: %d\n",led_fd);
printf("Content-type: text/html;charset=utf-8\n\n");
printf("<html>\n");
printf("<head><title>web 点灯</title></head>\n");
printf("<body>\n");
printf("<p>led is setted successful! you can watch the led's change</p>\n");
//printf("<p><a herf=http://192.168.1.100/led.html>go back</a></p>\n");
printf("<a href=\"/led_test.html\">go back led control page </a>");
printf("</body>\n");
data = getenv("QUERY_STRING"); //getenv()读取环境变量的当前值的函数
if(sscanf(data,"led_control=%d&led_state=%d",&led_control,&led_state)!=2)
{ //利用sscnaf()函数的特点将环境变量分别提取出led_control和led_state这两个值
printf("<p>please input right");
printf("</p>");
}
printf("<p>led_control = %d,led_state = %d</p>", led_control, led_state);
if(led_control < 2 || led_control > 5) {
printf("<p>Please input 2<=led_control<=5!");
printf("</p>");
}
if(led_state>1) {
printf("<p>Please input 0<=led_state<=1!");
printf("</p>");
}
led.led_num = led_control;
led.led_state = led_state;
if(led.led_state == 0){
//关灯
ioctl(led_fd,FS_LED_OFF, led.led_num);
}else if(led.led_state == 1){
//开灯
ioctl(led_fd,FS_LED_ON, led.led_num);
}else if(led.led_state == 2){
//流水灯
while(j --){
for(i = 2; i <= 5; i ++ ){
led.led_num = i;
led.led_state = 0;
ioctl(led_fd,FS_LED_OFF, led.led_num);
usleep(500000);
led.led_state = 1;
ioctl(led_fd,FS_LED_ON, led.led_num);
usleep(500000);
}
}
}
close(led_fd);
printf("</html>\n");
return 0;
}
arm-linux-gcc -o led_test.cgi led_test.c
编译生成cgi文件
4、HTML的编写
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>web点灯实验</title>
</head>
<body style="background-color: powderblue;">
<td height="500">
<div align="center">
<h1 align="center">基于Cortex-A9的web控制LED灯</h1>
<!--新建一个表单,动作链接到开发板的/cgi-bin/cgi_led.cgi,采用的方法为GET-->
<form action="/cgi-bin/led_test.cgi" method="get">
<p align="center">Web端的led的控制测试</p>
<p align="center">请输入需要控制的led:<input type="text" name="led_control"/></p>
<p align="center">请输入LED控制命令 :<input type="text" name="led_state"/></p>
<h2 align="center"> (0熄灭-1点亮-2流水)</h2>
<p align="center"><input type="submit" value="sure"/>
<input type="reset" value="back"/>
</p>
</form>
</div>
</td>
</body>
</html>
5、运行查看情况
4.文件拿到开发板执行
- 拷贝ko文件到开发板
cp led_test.ko /opt/4412/rootfs/drv/
- 拷贝cgi文件到开发板
cp led_test.cgi /opt/4412/rootfs/boa/cgi-bin/
- 拷贝HTML文件到开发板
cp led_test.html /opt/4412/rootfs/boa/www/
- 加载驱动模块
insmod led_test.ko
查看主设备号:cat /proc/devices/ | grep led_test
创建设备:mknod /dev/led_test c 主设备号 0
- boa服务器运行
./boa
- 网页端运行查看现象