目录
一.开发环境
开发板:
核心板:TQ210CoreB
底板:TQ210 V4
CPU:s5pv210
内核:Linux_kernel_3.0.8
交叉编译系统环境:
操作系统:ubnutu16.04
编译器:arm-embedsky-linux-gnueabi工具链 4.4.6
所需知识点:
若你是刚入门的学生不太看得懂原理图与芯片手册,请先看下这篇文章中针对电路原理与芯片文档这块的教程:详细介绍如何读懂STM32开发板电路原理图以及芯片文档和开发手册,并编写一个测试程序:点亮一个LED灯
本文还需要对Linux设备管理器有一定的了解,否则你开发时只知道调这些函数,但不知道内核态发生了什么,所以建议大家学一下相关知识点:Linux嵌入式开发_主设备号与次设备号详解、Linux驱动开发_设备文件系统详解
本文使用GIT来管理项目,GIT方面的教程:关于Git这一篇就够了
内核开发基本知识:Linux内核开发_内核模块
针对位的高级应用:c语言位操作的高级应用
HTTP方面:
这些知识点我都在别的文章中有详细的解释,若对单片机/嵌入式体系基础较差的同学可以看一看。
本篇教程较为类GNU/LINUX风格,从目录体系到项目管理,都会以类GNU/LINUX风格编写
即便是一个小demo我们也要用git来管理,这是为了加深大家对git的了解与认识,也为将来的工作做准备。
请大家先看完上面的知识在继续学习本篇知识。
二. 准备工作:
工欲善其事必先利其器,当我们开发一个项目工程时,需要构建好项目体系,这样便于我们后面的开发,也让我们的项目变得可维护性更高一点
1. 创建一个项目工程目录
mkdir moudul && cd moudul 
2. 创建输出与目标目录
arch、output
这两个目录将用于目标文件的输出以及中间文件的输出目录。
在arch目录下在新建一个arm的目录,因为我们的板子是arm架构,所以新建一个目录用来存放arm的目标文件,这样的体系是源于Linux内核目录体系
   
    - 
     
      
     
     
      
       mkdir arch && mkdir arch/arm
      
     
 
    - 
     
      
     
     
      
       mkdir output
      
     
 
   
 
3.头文件目录
在建立一个头文件目录,同时在include目录下创建一个目录TQ210_LED,代表工程类型,然后在这个目录下创建两个子目录:device、app,用来存放驱动/app的头文件
   
    - 
     
      
     
     
      
       mkdir include
      
     
 
    - 
     
      
     
     
      
       mkdir include/TQ210_LED
      
     
 
    - 
     
      
     
     
      
       mkdir include/TQ210_LED/device
      
     
 
    - 
     
      
     
     
      
       mkdir include/TQ210_LED/app
      
     
 
   
 
4. 建立源代码src目录
目录结构与头文件目录一致
   
    - 
     
      
     
     
      
       mkdir src
      
     
 
    - 
     
      
     
     
      
       mkdir src/TQ210_LED
      
     
 
    - 
     
      
     
     
      
       mkdir src/TQ210_LED/device
      
     
 
    - 
     
      
     
     
      
       mkdir src/TQ210_LED/app
      
     
 
   
 
好了到此我们的工程体系已经建立完成
   
    - 
     
      
     
     
      
       beis@ubuntu:~/moudul$ tree
      
     
 
    - 
     
      
     
     
      
       .
      
     
 
    - 
     
      
     
     
      
       ├── arch
      
     
 
    - 
     
      
     
     
      
       │   └── arm
      
     
 
    - 
     
      
     
     
      
       ├── include
      
     
 
    - 
     
      
     
     
      
       │   └── TQ210_LED
      
     
 
    - 
     
      
     
     
      
       │       ├── app
      
     
 
    - 
     
      
     
     
      
       │       └── device
      
     
 
    - 
     
      
     
     
      
       ├── output
      
     
 
    - 
     
      
     
     
      
       └── src
      
     
 
    - 
     
      
     
     
      
           └── TQ210_LED
      
     
 
    - 
     
      
     
     
      
               ├── app
      
     
 
    - 
     
      
     
     
      
               └── device
      
     
 
   
 
目录结构分配的非常清晰合理,当我们新增别的模块时,只需要按照这个规范在src与include下建立不同的类型目录就可以了
若别的架构只需要在arch下建立对应的架构目录即可。
目录体系完成之后我们在使用GIT来管理我们的项目
5. 使用git管理你的项目
git init 
三.编写LED驱动
在src/TQ210_LED/device目录下新建一个.c文件和在include/TQ210_LED/device目录下新建一个.h文件
   
    - 
     
      
     
     
      
       touch src/TQ210_LED/device/TQ210_LED_device.c
      
     
 
    - 
     
      
     
     
      
       touch include/TQ210_LED/device/TQ210_LED_device.h
      
     
 
   
 
然后使用vim打开.c文件我们就可以开始编写驱动模块啦
vim src/TQ210_LED/device/TQ210_LED_device.c 
现在我们打开电路原理图,这是开发步骤的必须的第一步,因为只有看原理图才知道电路的结构。

在原理图中找到LED这一块并放大

从原理图中可以得知LED1、LED2都接在GPC端口上。
 vdd表示器件内部工作电压的符号,vdd5v的意思就是至少需要5伏电压才能让此器件工作,不过这个我们一般不用关系,这个一般由PCB板设计者们已经完成了,输入源端已经设定好电阻之类的器件器件来控制电流通过,包括板子使用的电流模式。
 R开头的这样的电路符号一般都是电阻名字,1K=1000欧姆,也就是说当电压流过时它会吸收掉1000欧姆的电压。
  这是一个放大三极管,用于放大电流的,型号是S8050,符号是Q,Q1代表一号放大三极管。
从这里可以看到LED1和LED2分别接在GPC0号端口上,位为3与4
这里我教大家如何通过GPC0_4这个标识来找到芯片手册里的对应标志位
我们打开芯片手册,先看下GPIO的框架图

它由两部分组成 OFF PART(断电部分)和ALILVE PART(带电部分)
两者分别为,睡眠模式与非睡眠模式,也就是说这个GPIO框架支持睡眠模式与非睡眠模式,若进入睡眠模式则整个框架内部时钟将停止工作,等待其它中断将其唤醒,同时在睡眠模式下是能够保证其GPIO内部寄存器的值的。
它挂载在APB总线上,并且APB没有挂接到RCC这样的时钟总线上,不像STM32是挂接在这个总线上,若不先开启它则APB总线不会工作,这是STM32出于低功耗的设计。
接着我们找到GPC端口的描述

这里建议大家在打开PDF时使用CTRL+F去搜索GPC0能快速找到对应的说明页
按照单片机的开发的经验来说,我们可以知道若想让一个GPC口工作,必须使其设置为OUTPUT模式
同时可以看到DAT寄存器的介绍

DAT说明:
当端口配置为输入端口时,对应位为引脚状态当端口配置为输出端口时,引脚状态与对应位相同当端口配置为功能引脚时,将读取未定义的值。
也就是说CON对应端口是输出时,DAT对应的就是状态值,给1即高电平,给0即低电平。
在三星s5pv210中每个GPIO口由CON和DAT组成,CON是控制状态,DAT是输入输出状态。
并且我们可以看到GPC0CON的端口地址为:0xE0200060
GPC0DAT的端口地址为:0XE0200064 相差四字节。
一个32位机器上的int的大小。
基本上硬件以及地址信息我们都知道了,那么就是正式写代码开始进行开发了。
三.一 准备工作
在你的src/device目录下新建一个.c的文件
touch src/TQ210_LED/device/TQ210_device_led.c 
同时在创建头文件:
touch include/TQ210_LED/device/TQ210_device_led.c 
然后使用你喜欢的编辑工具开始写代码吧!
首先在.c文件中包含基本头文件:
   
    - 
     
      
     
     
      
       #include <linux/module.h>
      
     
 
    - 
     
      
     
     
      
       #include <linux/ioport.h>
      
     
 
    - 
     
      
     
     
      
       #include <linux/io.h>
      
     
 
    - 
     
      
     
     
      
       #include <linux/platform_device.h>
      
     
 
    - 
     
      
     
     
      
       #include <linux/init.h>
      
     
 
    - 
     
      
     
     
      
       #include <linux/serial_core.h>
      
     
 
    - 
     
      
     
     
      
       #include <linux/serial.h>
      
     
 
    - 
     
      
     
     
      
       #include <asm/irq.h>
      
     
 
    - 
     
      
     
     
      
       #include <mach/hardware.h>
      
     
 
    - 
     
      
     
     
      
       #include <plat/regs-serial.h>
      
     
 
    - 
     
      
     
     
      
       #include <mach/regs-gpio.h>
      
     
 
    - 
     
      
     
     
      
       #include <asm/uaccess.h>
      
     
 
   
 
基本信息:
   
    - 
     
      
     
     
      
       //author
      
     
 
    - 
     
      
     
     
      
       MODULE_AUTHOR(
       "Stephen Zhou");
      
     
 
    - 
     
      
     
     
      
       MODULE_LICENSE(
       "GPL");
      
     
 
   
 
init与exit:
   
    - 
     
      
     
     
      
       static int __init led_init(void){
      
     
 
    - 
     
      
     
     
       
      
     
 
    - 
     
      
     
     
      
       }
      
     
 
    - 
     
      
     
     
       
      
     
 
    - 
     
      
     
     
      
       static void __exit led_exit(void){
      
     
 
    - 
     
      
     
     
       
      
     
 
    - 
     
      
     
     
      
       }
      
     
 
    - 
     
      
     
     
       
      
     
 
    - 
     
      
     
     
      
       module_init(led_init);
      
     
 
    - 
     
      
     
     
      
       module_exit(led_exit);
      
     
 
   
 
我们在增加一些基本信息:
这些信息用来存储针对设备处理器用的名字,这些在之前的文件系统详解中都有详细说过
先定义存放/dev下的名字,这个是给udev看的:
   
    - 
     
      
     
     
      
       //led one and two name
      
     
 
    - 
     
      
     
     
      
       #define LED_ONE_NAME "TQ210_LED_ONE"
      
     
 
    - 
     
      
     
     
      
       #define LED_TWO_NAME "TQ210_LED_TWO"
      
     
 
   
 
定义内核模块表中的名字,以及sysfs的名字,这个是给内核和sysfs文件系统看的
   
    - 
     
      
     
     
      
       //led name in kernel
      
     
 
    - 
     
      
     
     
      
       #define LED_KERNEL_NAME "TQ210_LED"
      
     
 
    - 
     
      
     
     
      
       #define LED_SYSFS_CLASS_NAME "TQ210_LED_CLASS"
      
     
 
   
 
led数量,这个是给我们程序自己看的
   
    - 
     
      
     
     
      
       //led device number max
      
     
 
    - 
     
      
     
     
      
       #define LED_NUMBER_MAX 2
      
     
 
   
 
在写几个针对位操作的函数:
   
    - 
     
      
     
     
      
       //set or get gpic bit value
      
     
 
    - 
     
      
     
     
      
       #define SET_GPIC(GPIC_ADDRESS,VALUE,OPE) *GPIC_ADDRESS OPE VALUE
      
     
 
    - 
     
      
     
     
      
       #define SET_GPIC_STATE(GPIC_ADDRESS,LED1_VALUE,LED2_VALUE,OPE) *GPIC_ADDRESS OPE (LED1_VALUE | LED2_VALUE)
      
     
 
    - 
     
      
     
     
      
       #define GET_GPIC(GPIC_ADDRESS,VALUE) *GPIC_ADDRESS & VALUE
      
     
 
    - 
     
      
     
     
       
      
     
 
    - 
     
      
     
     
      
       //led gpic port
      
     
 
    - 
     
      
     
     
      
       #define LED1_GPIC_BIT(VALUE) (VALUE << 12)
      
     
 
    - 
     
      
     
     
      
       #define LED2_GPIC_BIT(VALUE) (VALUE << 16)
      
     
 
    - 
     
      
     
     
      
       #define LED1_GPIC_DAT_BIT(VALUE) (VALUE << 3)
      
     
 
    - 
     
      
     
     
      
       #define LED2_GPIC_DAT_BIT(VALUE) (VALUE << 4)
      
     
 
   
 
属性宏:
   
    - 
     
      
     
     
      
       //device gpic address
      
     
 
    - 
     
      
     
     
      
       #define GPIC0_CON_ADDRESS 0xE0200060
      
     
 
    - 
     
      
     
     
      
       #define GPIC0_DAT_ADDRESS 0xE0200064
      
     
 
    - 
     
      
     
     
      
       #define GPIC_ADDRESS_FORMAT 16
      
     
 
    - 
     
      
     
     
       
      
     
 
    - 
     
      
     
     
      
       //state
      
     
 
    - 
     
      
     
     
      
       #define LED_STATE_ON "ON"
      
     
 
    - 
     
      
     
     
      
       #define LED_STATE_OFF "OFF"
      
     
 
   
 
好了,接下来我们去实现init函数
三.二 init实现
在实现init函数之前我们在.c文件中申请几个全局变量,用来存储不同的属性:
存储led名字:
char LED_NAME[][256] = {{LED_ONE_NAME},{LED_TWO_NAME}}; 
存储con与dat地址:
   
    - 
     
      
     
     
      
       volatile 
       unsigned 
       long* GPIC0_address = 
       NULL;
      
     
 
    - 
     
      
     
     
      
       volatile 
       unsigned 
       long* GPIC0_dat     = 
       NULL;
      
     
 
   
 
存储内核fd与类(sysfs)fd:
   
    - 
     
      
     
     
      
       //drive struct
      
     
 
    - 
     
      
     
     
      
       static 
       int                         led_kernel_fd                                 = 
       0;                           
       //kernel struct fd
      
     
 
    - 
     
      
     
     
      
       static 
       struct class* led_device_file_class = NULL;                        
       //sysfs fd
      
     
 
    - 
     
      
     
     
      
       static 
       struct device* led_device_class_son[LED_NUMBER_MAX] = {
       NULL};       
      
     
 
   
 
除此之外还需要一个结构体:
struct file_operations 
这个结构体就是用来存储文件函数指针的,write、open等函数实现
为此我们先将write、open先定义出来,什么都不做,后面我们在实现:
   
    - 
     
      
     
     
      
       //open
      
     
 
    - 
     
      
     
     
      
       static int led_open(struct inode* inode,struct file* file){
      
     
 
    - 
     
      
     
     
       
      
     
 
    - 
     
      
     
     
      
       }
      
     
 
    - 
     
      
     
     
       
      
     
 
    - 
     
      
     
     
      
       //write
      
     
 
    - 
     
      
     
     
      
       static ssize_t led_write(struct file* file,const char __user* buf,size_t count,loff_t* ppos){
      
     
 
    - 
     
      
     
     
       
      
     
 
    - 
     
      
     
     
      
       }
      
     
 
    - 
     
      
     
     
       
      
     
 
    - 
     
      
     
     
      
       //read
      
     
 
    - 
     
      
     
     
      
       static ssize_t led_read(struct file* file,char __user* buf,size_t count,loff_t* ppos){
      
     
 
    - 
     
      
     
     
       
      
     
 
    - 
     
      
     
     
      
       }
      
     
 
    - 
     
      
     
     
       
      
     
 
    - 
     
      
     
     
      
       //ioctl
      
     
 
    - 
     
      
     
     
      
       static long led_ioctl(struct file* file,unsigned int cmd,unsigned long arg){
      
     
 
    - 
     
      
     
     
       
      
     
 
    - 
     
      
     
     
      
       }
      
     
 
   
 
注意根据内核版本不同,在内核部分的write、read函数原型不同,可以根据自己开发板子使用的内核版本来查一下。
我们把fops结构体定义出来:
   
    - 
     
      
     
     
      
       //drive struct
      
     
 
    - 
     
      
     
     
      
       static 
       struct file_operations led_drive_fops = {
      
     
 
    - 
     
      
     
     
       
      
     
 
    - 
     
      
     
     
      
                       .owner          = THIS_MODULE,
      
     
 
    - 
     
      
     
     
      
                       .open           = led_open,
      
     
 
    - 
     
      
     
     
      
                       .write          = led_write,
      
     
 
    - 
     
      
     
     
      
                       .read           = led_read,
      
     
 
    - 
     
      
     
     
      
                       .unlocked_ioctl = led_ioctl,
      
     
 
    - 
     
      
     
     
      
       };
      
     
 
   
 
里面的THIS_MODULE是一个地址,这个会在预编译期间被编译器替换为当前模块的地址,也就是说它指向当前模块。
init函数的目的:
将设备注册到内核并注册到类文件系统,将udev注册到dev dir
第一步注册到内核结构体中
注意在内核模块中我们尽量多打日志,利用printk函数,便于我们调试
因为内核态是不能用GDB这些来调试的。
   
    - 
     
      
     
     
      
       led_kernel_fd = register_chrdev(
       0,LED_KERNEL_NAME,&led_drive_fops);
      
     
 
    - 
     
      
     
     
              
       if(led_kernel_fd < 
       0){
      
     
 
    - 
     
      
     
     
      
                       printk(
       "TQ210_LED[ERROR]: register_chrdev - %d\n",led_kernel_fd);
      
     
 
    - 
     
      
     
     
                      
       return 
       -1;
      
     
 
    - 
     
      
     
     
      
       }
      
     
 
   
 
第二步 注册到sysfs/sys/class dir,申请一个父节点
led_device_file_class = class_create(THIS_MODULE,LED_SYSFS_CLASS_NAME); 
第三步注册子设备到类sysfs中,作为子节点
注意编译器是c99,我们不能在代码部分声明变量,所以在开头加一个i的定义
 int i = 0;      //for c99  
   
    - 
     
      
     
     
       
       for(; i < LED_NUMBER_MAX; ++i){
      
     
 
    - 
     
      
     
     
      
                       led_device_class_son[i] = device_create (led_device_file_class,
       NULL,MKDEV(led_kernel_fd,i),
       NULL,LED_NAME[i]);
      
     
 
    - 
     
      
     
     
                      
       if(unlikely(IS_ERR(led_device_class_son[i]))) {
      
     
 
    - 
     
      
     
     
      
                               printk(
       "TQ210_LED[ERROR]: register son device\n");
      
     
 
    - 
     
      
     
     
                              
       return 
       -2;
      
     
 
    - 
     
      
     
     
      
                       }
      
     
 
    - 
     
      
     
     
      
               }
      
     
 
    - 
     
      
     
     
       
      
     
 
   
 
最后一步将物理地址映射到内核虚拟地址:
操作系统出于alu地址随机化保护原因,是不能直接访问物理地址的,需要先转化为虚拟地址。
   
    - 
     
      
     
     
      
        GPIC0_address = (
       volatile 
       unsigned 
       long*)ioremap(GPIC0_CON_ADDRESS,GPIC_ADDRESS_FORMAT);
      
     
 
    - 
     
      
     
     
      
        GPIC0_dat     = (
       volatile 
       unsigned 
       long*)ioremap(GPIC0_DAT_ADDRESS,GPIC_ADDRESS_FORMAT);
      
     
 
   
 
最后打印一下表示我们成功初始化init了。
   
    - 
     
      
     
     
      
       printk(
       "TQ210_LED[SUCCESS]:init\n");
      
     
 
    - 
     
      
     
     
      
       return 0;
      
     
 
   
 
在实现一下exit函数:
三.四 exit实现
先释放子类节点,注意这里的释放是有顺序之分的
因为我们在注册的时候是先注册到内核-sysfs-sysfs子节点这样的一个流程。
在设备文件详解里我说过它的寻找方式,当我们open一个/dev下的文件时,先调用中断的do_sys_open,然后在调用do_filp_open,来从文件系统中获取节点,VFS中,因为/dev目录下的文件是存在于磁盘上的,但是这个文件都在VFS的文件描述结构体中又注册,因为VFS是管理磁盘的,然后在调用open_namei函数来根据name在VFS中获取指向这个文件的指针。
最后通过这个指针可以找到这个文件指向哪个open、write等,最后会发现通过它里面的文件指针找到的是sysfs,udev只负责根据class目录下的结构把文件注册到VFS的磁盘/DEV目录下。
然后sysfs里的节点指向内核中的节点,所以当我们想删除一个节点的话,如果先删除的是内核里的节点的话,你在去删除sysfs里的节点会发现出现段错误。
这个原因大概是因为找到sysfs节点时,linux会判断指向的内核指针是否有效,来确认这是否是一个正常的设备。
所以我们怎么注册的,就怎么反着来释放。
先释放子节点
   
    - 
     
      
     
     
      
       int i = 
       0;      
       //for c99
      
     
 
    - 
     
      
     
     
       
      
     
 
    - 
     
      
     
     
      
       for(; i < LED_NUMBER_MAX; ++i){
      
     
 
    - 
     
      
     
     
      
                       device_unregister(led_device_class_son[i]);
      
     
 
    - 
     
      
     
     
      
       }
      
     
 
    - 
     
      
     
     
       
      
     
 
   
 
释放父节点
class_destroy(led_device_file_class);
 
关闭io映射
   
    - 
     
      
     
     
      
       iounmap(GPIC0_address);
      
     
 
    - 
     
      
     
     
      
       iounmap(GPIC0_dat);
      
     
 
   
 
删除内核里的模块信息
 unregister_chrdev(led_kernel_fd,LED_KERNEL_NAME); 
在打印一行log
 printk("TQ210_LED[SUCCESS]:exit\n"); 
在打印时非常建议大家在前面加上标识符,这样在输出log时使用grep能更清楚看到你的日志。
到这里你的雏形驱动已经完成了,你编译好之后在使用insmod命令安装的话就会看到/dev目录下对应的节点。
当使用sysfs注册时,sysfs会自动通知udev的。
现在开始写make,回到顶层目录下
三.五 make实现
内核模块的make写法已经在之前的linux内核模块文章中说过了
这里交叉编译的话记得修改KDIR还有CROSS_COMPILE的变量值就可以了
   
    - 
     
      
     
     
      
       ifneq ($(KERNELRELEASE),)
      
     
 
    - 
     
      
     
     
      
               obj-m  := ./src/TQ210_LED/device/TQ210_device_led.o
      
     
 
    - 
     
      
     
     
      
       else
      
     
 
    - 
     
      
     
     
      
               KDIR := /home/beis/TQ/opt/EmbedSky/TQ210/Kernel_3
       .0
       .8_TQ210_for_Linux_v2
       .4
      
     
 
    - 
     
      
     
     
      
       all:
      
     
 
    - 
     
      
     
     
      
               $(MAKE) -C $(KDIR) M=$(PWD) modules CROSS_COMPILE=arm-embedsky-linux-gnueabi-
      
     
 
    - 
     
      
     
     
      
               mv ./src/TQ210_LED/device
       /*.mod.c ./output
      
     
 
    - 
     
      
     
     
      
        mv ./src/TQ210_LED/device/*.ko ./arch/arm/TQ210_LED
      
     
 
    - 
     
      
     
     
      
       clean:
      
     
 
    - 
     
      
     
     
      
        $(MAKE) -C $(KDIR) M=$(PWD) clean
      
     
 
    - 
     
      
     
     
      
        rm ./output/*
      
     
 
    - 
     
      
     
     
      
       endif
      
     
 
   
 
然后你在make一下,就可以在arch/arm/TQ210_LED目录下看到你的ko文件了。
完成初步之后就需要git保存一下啦
   
    - 
     
      
     
     
      
       git add .
      
     
 
    - 
     
      
     
     
      
       git commit -m 
       "one"
      
     
 
   
 
那么接下来回到驱动文件下实现open函数
三. 六 open实现
首先我们可以利用MINOR取的当前操作的子节点
 int son_id = MINOR(inode->i_rdev); 
然后利用switch来对不同节点做不同的处理,这样就不用写多个驱动文件了
同一驱动类型,非常建议这么做。
   
    - 
     
      
     
     
      
       switch(son_id){
      
     
 
    - 
     
      
     
     
       
      
     
 
    - 
     
      
     
     
                              
       case 
       0: 
       //led one
      
     
 
    - 
     
      
     
     
                
      
     
 
    - 
     
      
     
     
                              
       break;
      
     
 
    - 
     
      
     
     
       
      
     
 
    - 
     
      
     
     
                              
       case 
       1: 
       //led two
      
     
 
    - 
     
      
     
     
       
      
     
 
    - 
     
      
     
     
                              
       break;
      
     
 
    - 
     
      
     
     
       
      
     
 
    - 
     
      
     
     
                              
       default:
      
     
 
    - 
     
      
     
     
      
                                       printk(
       "TQ210_LED[ERROR]: error son device number\n");
      
     
 
    - 
     
      
     
     
                                      
       return 
       -1;
      
     
 
    - 
     
      
     
     
                              
       break;
      
     
 
    - 
     
      
     
     
       
      
     
 
    - 
     
      
     
     
      
                       }
      
     
 
   
 
首先利用我们写好的位函数,把led开启成输出模式
首先是先清空,在开启,这样方便进行位运算,从而不被其他位影响。
   
    - 
     
      
     
     
      
       case 
       0: 
       //led one
      
     
 
    - 
     
      
     
     
      
               SET_GPIC(GPIC0_address,~LED1_GPIC_BIT(
       0xf),&=);         
       //clear
      
     
 
    - 
     
      
     
     
      
               SET_GPIC(GPIC0_address,LED1_GPIC_BIT(
       0x1),|=);          
       //output
      
     
 
    - 
     
      
     
     
      
       break;
      
     
 
    - 
     
      
     
     
       
      
     
 
    - 
     
      
     
     
      
       case 
       1: 
       //led two
      
     
 
    - 
     
      
     
     
      
               SET_GPIC(GPIC0_address,~LED2_GPIC_BIT(
       0xf),&=);         
       //clear
      
     
 
    - 
     
      
     
     
      
               SET_GPIC(GPIC0_address,LED2_GPIC_BIT(
       0x1),|=);          
       //output
      
     
 
    - 
     
      
     
     
      
       break;
      
     
 
   
 
针对初学者,可能对位还不是特别懂,这里我给大家拆一下,详细说一下这个步骤
把宏展开给大家看下,以LED1举例
*GPIC0_address &= ~((0xf<<(12)); 
上面原理图说过,CON3[15:12]是设置模式的,0xf的二进制是1111,左移12位是1111000000000000,然后在与原位做与运算,与运算特点:两位同时为1则为1,不相同则为0
然后这里取反就是0000111111111111,与CON3做与运算:
假设CON3是1011000000001000
1011000000001000
——————————
00001111111111110
——————————
0000000000001000
可以看到非常巧妙的利用与特点,没有修改其它位把我们想要设置的位给清空了。
第二个也与之一样,在你对这些含糊不清的时候,请拿起笔来自己运算,让自己清楚才是真正明白了。
*GPIC0_address |= ((0x1<<(12)); 
左移12位:0001000000000000 ,或运算的特点:当一个bit位为1,则为1,所以不用做取反运算了。
0000000000001000
——————————
0001000000000000
——————————
0001000000001000
也在没有修改其它位的情况下完成了。
open实现完整代码:
   
    - 
     
      
     
     
      
       static int led_open(struct inode* inode,struct file* file){
      
     
 
    - 
     
      
     
     
       
      
     
 
    - 
     
      
     
     
                      
       /* open device and init */
      
     
 
    - 
     
      
     
     
       
      
     
 
    - 
     
      
     
     
                      
       //get son device number
      
     
 
    - 
     
      
     
     
                      
       int son_id = MINOR(inode->i_rdev);
      
     
 
    - 
     
      
     
     
       
      
     
 
    - 
     
      
     
     
                      
       switch(son_id){
      
     
 
    - 
     
      
     
     
       
      
     
 
    - 
     
      
     
     
                              
       case 
       0: 
       //led one
      
     
 
    - 
     
      
     
     
      
                                       SET_GPIC(GPIC0_address,~LED1_GPIC_BIT(
       0xf),&=);         
       //clear
      
     
 
    - 
     
      
     
     
      
                                       SET_GPIC(GPIC0_address,LED1_GPIC_BIT(
       0x1),|=);          
       //output
      
     
 
    - 
     
      
     
     
                              
       break;
      
     
 
    - 
     
      
     
     
       
      
     
 
    - 
     
      
     
     
                              
       case 
       1: 
       //led two
      
     
 
    - 
     
      
     
     
      
                                       SET_GPIC(GPIC0_address,~LED2_GPIC_BIT(
       0xf),&=);         
       //clear
      
     
 
    - 
     
      
     
     
      
                                       SET_GPIC(GPIC0_address,LED2_GPIC_BIT(
       0x1),|=);          
       //output
      
     
 
    - 
     
      
     
     
                              
       break;
      
     
 
    - 
     
      
     
     
       
      
     
 
    - 
     
      
     
     
                              
       default:
      
     
 
    - 
     
      
     
     
      
                                       printk(
       "TQ210_LED[ERROR]: error son device number\n");
      
     
 
    - 
     
      
     
     
                                      
       return 
       -1;
      
     
 
    - 
     
      
     
     
                              
       break;
      
     
 
    - 
     
      
     
     
       
      
     
 
    - 
     
      
     
     
      
                       }
      
     
 
    - 
     
      
     
     
       
      
     
 
    - 
     
      
     
     
      
                       printk(
       "TQ210_LED[SUCCESS]:open\n");
      
     
 
    - 
     
      
     
     
                      
       return 
       0;
      
     
 
    - 
     
      
     
     
      
       }
      
     
 
   
 
三.七 write实现
获取子节点
 int son_id = MINOR(file->f_dentry->d_inode->i_rdev); 
这里我们利用一个函数从用户态往内核态拿一下参数,记得失败打log
   
    - 
     
      
     
     
      
       char val = 
       0;
      
     
 
    - 
     
      
     
     
      
       if(copy_from_user(&val,buf,count)){ printk(
       "TQ210_LED[ERROR]:get user variable\n"); 
       return 
       -1; }
      
     
 
   
 
然后实现:
就是判断写入的是1则亮,0则灭,位运算在open已经仔细说过了,这里就不展开说了。
我们这里的操作要对dat寄存器,上面开头也说过了。
   
    - 
     
      
     
     
      
       switch(son_id){
      
     
 
    - 
     
      
     
     
       
      
     
 
    - 
     
      
     
     
                              
       case 
       0: 
       //led one
      
     
 
    - 
     
      
     
     
                                      
       if(val == 
       1){ SET_GPIC_STATE(GPIC0_dat,LED1_GPIC_DAT_BIT(
       1),LED2_GPIC_DAT_BIT((GET_GPIC(GPIC0_dat,LED2_GPIC_DAT_BIT(
       1)))),|=); }
       else{ SET_GPIC_STATE(GPIC0_dat,~LED1_GPIC_DAT_BIT(
       1),(GET_GPIC(GPIC0_dat,
       0)),&=); }
      
     
 
    - 
     
      
     
     
      
                                       printk(
       "TQ210_LED[MSG]:led one\n");
      
     
 
    - 
     
      
     
     
                              
       break;
      
     
 
    - 
     
      
     
     
       
      
     
 
    - 
     
      
     
     
                              
       case 
       1: 
       //led two
      
     
 
    - 
     
      
     
     
                                      
       if(val == 
       1){ SET_GPIC_STATE(GPIC0_dat,LED2_GPIC_DAT_BIT(
       1),LED1_GPIC_DAT_BIT(GET_GPIC(GPIC0_dat,LED1_GPIC_DAT_BIT(
       1))),|=); }
       else{ SET_GPIC_STATE(GPIC0_dat,~LED2_GPIC_DAT_BIT(
       1),(GET_GPIC(GPIC0_dat,
       0)),&=); }
      
     
 
    - 
     
      
     
     
      
                                       printk(
       "TQ210_LED[MSG]:led two\n");
      
     
 
    - 
     
      
     
     
                              
       break;
      
     
 
    - 
     
      
     
     
       
      
     
 
    - 
     
      
     
     
                              
       default:
      
     
 
    - 
     
      
     
     
      
                                       printk(
       "TQ210[ERROR]:can't write device number\n");
      
     
 
    - 
     
      
     
     
                              
       break;
      
     
 
    - 
     
      
     
     
       
      
     
 
    - 
     
      
     
     
      
       }
      
     
 
   
 
最后打印一下:
   
    - 
     
      
     
     
      
        printk(
       "TQ210_LED[SUCCESS]:write\n");
      
     
 
    - 
     
      
     
     
       
       return 
       0;
      
     
 
   
 
完整实现:
   
    - 
     
      
     
     
      
       static ssize_t led_write(struct file* file,const char __user* buf,size_t count,loff_t* ppos){
      
     
 
    - 
     
      
     
     
       
      
     
 
    - 
     
      
     
     
                      
       //write device
      
     
 
    - 
     
      
     
     
       
      
     
 
    - 
     
      
     
     
                      
       //get son device number
      
     
 
    - 
     
      
     
     
                      
       int son_id = MINOR(file->f_dentry->d_inode->i_rdev);
      
     
 
    - 
     
      
     
     
       
      
     
 
    - 
     
      
     
     
                      
       //get user variable to kernel variablei
      
     
 
    - 
     
      
     
     
                      
       char val = 
       0;
      
     
 
    - 
     
      
     
     
                      
       if(copy_from_user(&val,buf,count)){ printk(
       "TQ210_LED[ERROR]:get user variable\n"); 
       return 
       -1; }
      
     
 
    - 
     
      
     
     
       
      
     
 
    - 
     
      
     
     
                      
       switch(son_id){
      
     
 
    - 
     
      
     
     
       
      
     
 
    - 
     
      
     
     
                              
       case 
       0: 
       //led one
      
     
 
    - 
     
      
     
     
                                      
       if(val == 
       1){ SET_GPIC_STATE(GPIC0_dat,LED1_GPIC_DAT_BIT(
       1),LED2_GPIC_DAT_BIT((GET_GPIC(GPIC0_dat,LED2_GPIC_DAT_BIT(
       1)))),|=); }
       else{ SET_GPIC_STATE(GPIC0_dat,~LED1_GPIC_DAT_BIT(
       1),(GET_GPIC(GPIC0_dat,
       0)),&=); }
      
     
 
    - 
     
      
     
     
      
                                       printk(
       "TQ210_LED[MSG]:led one\n");
      
     
 
    - 
     
      
     
     
                              
       break;
      
     
 
    - 
     
      
     
     
       
      
     
 
    - 
     
      
     
     
                              
       case 
       1: 
       //led two
      
     
 
    - 
     
      
     
     
                                      
       if(val == 
       1){ SET_GPIC_STATE(GPIC0_dat,LED2_GPIC_DAT_BIT(
       1),LED1_GPIC_DAT_BIT(GET_GPIC(GPIC0_dat,LED1_GPIC_DAT_BIT(
       1))),|=); }
       else{ SET_GPIC_STATE(GPIC0_dat,~LED2_GPIC_DAT_BIT(
       1),(GET_GPIC(GPIC0_dat,
       0)),&=); }
      
     
 
    - 
     
      
     
     
      
                                       printk(
       "TQ210_LED[MSG]:led two\n");
      
     
 
    - 
     
      
     
     
                              
       break;
      
     
 
    - 
     
      
     
     
       
      
     
 
    - 
     
      
     
     
                              
       default:
      
     
 
    - 
     
      
     
     
      
                                       printk(
       "TQ210[ERROR]:can't write device number\n");
      
     
 
    - 
     
      
     
     
                              
       break;
      
     
 
    - 
     
      
     
     
       
      
     
 
    - 
     
      
     
     
      
                       }
      
     
 
    - 
     
      
     
     
       
      
     
 
    - 
     
      
     
     
      
                       printk(
       "TQ210_LED[SUCCESS]:write\n");
      
     
 
    - 
     
      
     
     
                      
       return 
       0;
      
     
 
    - 
     
      
     
     
       
      
     
 
    - 
     
      
     
     
      
       }
      
     
 
   
 
三.八 read实现
read实现也很简单,就是利用位运算去读位的值,唯一用到的就是用户态向内核态传递参数的函数:copy_to_user
完整代码:
   
    - 
     
      
     
     
      
       static ssize_t led_read(struct file* file,char __user* buf,size_t count,loff_t* ppos){
      
     
 
    - 
     
      
     
     
       
      
     
 
    - 
     
      
     
     
                      
       //read led state
      
     
 
    - 
     
      
     
     
       
      
     
 
    - 
     
      
     
     
                      
       int son_id = MINOR(file->f_dentry->d_inode->i_rdev);
      
     
 
    - 
     
      
     
     
       
      
     
 
    - 
     
      
     
     
                      
       int BIT = 
       0;
      
     
 
    - 
     
      
     
     
                      
       char on[
       2]  = LED_STATE_ON;
      
     
 
    - 
     
      
     
     
                      
       char off[
       3] = LED_STATE_OFF;
      
     
 
    - 
     
      
     
     
                      
       switch(son_id){
      
     
 
    - 
     
      
     
     
       
      
     
 
    - 
     
      
     
     
                              
       case 
       0:
      
     
 
    - 
     
      
     
     
      
                                       BIT = GET_GPIC(GPIC0_dat,LED1_GPIC_DAT_BIT(
       1));
      
     
 
    - 
     
      
     
     
                                      
       if(BIT){
      
     
 
    - 
     
      
     
     
       
      
     
 
    - 
     
      
     
     
                                              
       if(copy_to_user((
       char*)buf,&on,
       sizeof(on))) 
       return -EFAULT;
      
     
 
    - 
     
      
     
     
       
      
     
 
    - 
     
      
     
     
      
                                       }
       else{
      
     
 
    - 
     
      
     
     
                                              
       if(copy_to_user((
       char*)buf,&off,
       sizeof(off))) 
       return -EFAULT;
      
     
 
    - 
     
      
     
     
      
                                       }
      
     
 
    - 
     
      
     
     
                              
       break;
      
     
 
    - 
     
      
     
     
       
      
     
 
    - 
     
      
     
     
                              
       case 
       1:
      
     
 
    - 
     
      
     
     
      
                                       BIT = GET_GPIC(GPIC0_dat,LED2_GPIC_DAT_BIT(
       1));
      
     
 
    - 
     
      
     
     
                                      
       if(BIT){
      
     
 
    - 
     
      
     
     
       
      
     
 
    - 
     
      
     
     
                                              
       if(copy_to_user((
       char*)buf,&on,
       sizeof(on))) 
       return -EFAULT;
      
     
 
    - 
     
      
     
     
                                              
       return 
       2;
      
     
 
    - 
     
      
     
     
       
      
     
 
    - 
     
      
     
     
      
                                       }
       else{
      
     
 
    - 
     
      
     
     
                                              
       if(copy_to_user((
       char*)buf,&off,
       sizeof(off))) 
       return -EFAULT;
      
     
 
    - 
     
      
     
     
                                              
       return 
       3;
      
     
 
    - 
     
      
     
     
      
                                       }
      
     
 
    - 
     
      
     
     
                              
       break;
      
     
 
    - 
     
      
     
     
       
      
     
 
    - 
     
      
     
     
                              
       default:
      
     
 
    - 
     
      
     
     
      
                                       printk(
       "TQ210[ERROR]:can't write device number\n");
      
     
 
    - 
     
      
     
     
                                      
       return 
       -1;
      
     
 
    - 
     
      
     
     
                              
       break;
      
     
 
    - 
     
      
     
     
       
      
     
 
    - 
     
      
     
     
      
                       }
      
     
 
    - 
     
      
     
     
       
      
     
 
    - 
     
      
     
     
      
                       printk(
       "TQ210_LED[SUCCESS]:read\n");
      
     
 
    - 
     
      
     
     
                      
       return 
       0;
      
     
 
    - 
     
      
     
     
      
       }
      
     
 
   
 
三.九 ioctl实现
ioctl的实现的话,linux是有要求的,ioctl思想是利用参数来获取对应属性,实现对应功能。
linux内核是利用命令码实现这些,开发者利用switch case来对不同的命令码进行不同的实现
在内核里一个命令码是这样的:
   
    - 
     
      
     
     
      
       ________________________________________
      
     
 
    - 
     
      
     
     
       
      
     
 
    - 
     
      
     
     
      
       | 设备类型  | 序列号 |  方向 | 数据尺寸  |
      
     
 
    - 
     
      
     
     
       
      
     
 
    - 
     
      
     
     
      
       |----------|--------|------|--------     |
      
     
 
    - 
     
      
     
     
       
      
     
 
    - 
     
      
     
     
      
       | 
       8 bit   |  
       8 bit   | 
       2 bit |
       8~
       14 bit |
      
     
 
    - 
     
      
     
     
       
      
     
 
    - 
     
      
     
     
      
       |----------|--------|------|------------ |
      
     
 
   
 
linux内核也提供了一些实现宏定义
   
    - 
     
      
     
     
      
       //nr为序号,datatype为数据类型,如int
      
     
 
    - 
     
      
     
     
      
       _IO(type, nr ) 
       //没有参数的命令
      
     
 
    - 
     
      
     
     
      
       _IOR(type, nr, datatype) 
       //从驱动中读数据
      
     
 
    - 
     
      
     
     
      
       _IOW(type, nr, datatype) 
       //写数据到驱动
      
     
 
    - 
     
      
     
     
      
       _IOWR(type,nr, datatype) 
       //双向传送
      
     
 
   
 
例子:
   
    - 
     
      
     
     
      
       #define MEM_IOC_MAGIC 'm' //定义类型
      
     
 
    - 
     
      
     
     
      
       #define MEM_IOCSET _IOW(MEM_IOC_MAGIC,0,int)
      
     
 
    - 
     
      
     
     
      
       #define MEM_IOCGQSET _IOR(MEM_IOC_MAGIC, 1, int)
      
     
 
   
 
linux也提供了一些判断参数是否有效的宏函数
   
    - 
     
      
     
     
      
       _IOC_NR()    读取基数域值 (bit0~ bit7)
      
     
 
    - 
     
      
     
     
      
       _IOC_TYPE    读取魔数域值 (bit8 ~ bit15)
      
     
 
    - 
     
      
     
     
      
       _IOC_SIZE    读取数据大小域值 (bit16 ~ bit29)
      
     
 
    - 
     
      
     
     
      
       _IOC_DIR     获取读写属性域值 (bit30 ~ bit31)
      
     
 
   
 
我的定义:
   
    - 
     
      
     
     
      
       //ioctl
      
     
 
    - 
     
      
     
     
      
       #define MEMDEV_IOC_MAGIC 's'
      
     
 
    - 
     
      
     
     
      
       #define MEMDEV_IOCPRINT _IO(MEMDEV_IOC_MAGIC, 1)
      
     
 
    - 
     
      
     
     
      
       #define MEMDEV_IOCGETDATA _IOR(MEMDEV_IOC_MAGIC, 2, int)
      
     
 
    - 
     
      
     
     
      
       #define MEMDEV_IOCSETDATA _IOW(MEMDEV_IOC_MAGIC, 3, int)
      
     
 
    - 
     
      
     
     
      
       #define MEMDEV_IOC_MAXNR 3
      
     
 
   
 
依旧取子节点
   
    - 
     
      
     
     
      
       int par = 
       0;
      
     
 
    - 
     
      
     
     
      
       int son_id = MINOR(file->f_dentry->d_inode->i_rdev);
      
     
 
   
 
判断类型是否有效:
   
    - 
     
      
     
     
       
       if(_IOC_TYPE(cmd) != MEMDEV_IOC_MAGIC)
      
     
 
    - 
     
      
     
     
                              
       return -EINVAL;
      
     
 
   
 
判断参数是否有效
   
    - 
     
      
     
     
       
       if(_IOC_NR(cmd) > MEMDEV_IOC_MAXNR)
      
     
 
    - 
     
      
     
     
                              
       return -EINVAL;
      
     
 
   
 
在判断读写属性是否有效
   
    - 
     
      
     
     
       
       if (_IOC_DIR(cmd) & _IOC_READ){ 
       if(access_ok(VERIFY_WRITE, (
       void *)arg, _IOC_SIZE(cmd))){ 
       return -EFAULT; } }
      
     
 
    - 
     
      
     
     
                      
       else 
       if(_IOC_DIR(cmd) & _IOC_WRITE) { 
       if (access_ok(VERIFY_READ, (
       void *)arg, _IOC_SIZE(cmd))){ 
       return -EFAULT; } }
      
     
 
   
 
这里利用了一个access_ok是一个宏,用来判断参数的读写是否有效。
接下来就是具体实现啦
只需要判断cmd的属性就可以了。
   
    - 
     
      
     
     
       
       switch(son_id){
      
     
 
    - 
     
      
     
     
       
      
     
 
    - 
     
      
     
     
                              
       case 
       0:
      
     
 
    - 
     
      
     
     
                                      
       switch(cmd){
      
     
 
    - 
     
      
     
     
       
      
     
 
    - 
     
      
     
     
                                              
       case MEMDEV_IOCPRINT:   
       //Print information
      
     
 
    - 
     
      
     
     
      
                                                       printk(
       "TQ210_LED1 demo to stephen zhou\n");
      
     
 
    - 
     
      
     
     
                                              
       break;
      
     
 
    - 
     
      
     
     
       
      
     
 
    - 
     
      
     
     
                                              
       case MEMDEV_IOCGETDATA: 
       //Get parameters
      
     
 
    - 
     
      
     
     
                                                      
       return __put_user(par,(
       int*) arg);
      
     
 
    - 
     
      
     
     
                                              
       break;
      
     
 
    - 
     
      
     
     
       
      
     
 
    - 
     
      
     
     
                                              
       case MEMDEV_IOCSETDATA: 
       //Set parameters
      
     
 
    - 
     
      
     
     
                                                      
       return __get_user(par,(
       int*) arg);
      
     
 
    - 
     
      
     
     
                                              
       break;
      
     
 
    - 
     
      
     
     
       
      
     
 
    - 
     
      
     
     
                                              
       default:
      
     
 
    - 
     
      
     
     
                                                      
       return -EINVAL;
      
     
 
    - 
     
      
     
     
                                              
       break;
      
     
 
    - 
     
      
     
     
      
                                       }
      
     
 
    - 
     
      
     
     
                              
       break;
      
     
 
    - 
     
      
     
     
       
      
     
 
    - 
     
      
     
     
                              
       case 
       1:
      
     
 
    - 
     
      
     
     
                                      
       switch(cmd){
      
     
 
    - 
     
      
     
     
       
      
     
 
    - 
     
      
     
     
                                              
       case MEMDEV_IOCPRINT:   
       //Print information
      
     
 
    - 
     
      
     
     
      
                                                       printk(
       "TQ210_LED2 demo to stephen zhou\n");
      
     
 
    - 
     
      
     
     
                                              
       break;
      
     
 
    - 
     
      
     
     
       
      
     
 
    - 
     
      
     
     
                                              
       case MEMDEV_IOCGETDATA: 
       //Get parameters
      
     
 
    - 
     
      
     
     
                                                      
       return __put_user(par,(
       int*) arg);
      
     
 
    - 
     
      
     
     
                                              
       break;
      
     
 
    - 
     
      
     
     
       
      
     
 
    - 
     
      
     
     
                                              
       case MEMDEV_IOCSETDATA: 
       //Set parameters
      
     
 
    - 
     
      
     
     
                                                      
       return __get_user(par,(
       int*) arg);
      
     
 
    - 
     
      
     
     
                                              
       break;
      
     
 
    - 
     
      
     
     
       
      
     
 
    - 
     
      
     
     
                                              
       default:
      
     
 
    - 
     
      
     
     
                                                      
       return -EINVAL;
      
     
 
    - 
     
      
     
     
                                              
       break;
      
     
 
    - 
     
      
     
     
      
                                       }
      
     
 
    - 
     
      
     
     
                              
       break;
      
     
 
    - 
     
      
     
     
      
       }
      
     
 
   
 
最后别忘记打印:
   
    - 
     
      
     
     
      
        printk(
       "TQ210_LED[SUCCESS]:ioctl\n");
      
     
 
    - 
     
      
     
     
       
       return 
       0;
      
     
 
   
 
完整代码:
   
    - 
     
      
     
     
      
       static long led_ioctl(struct file* file,unsigned int cmd,unsigned long arg){
      
     
 
    - 
     
      
     
     
       
      
     
 
    - 
     
      
     
     
                      
       int par = 
       0;
      
     
 
    - 
     
      
     
     
                      
       //get son id 
      
     
 
    - 
     
      
     
     
                      
       int son_id = MINOR(file->f_dentry->d_inode->i_rdev);
      
     
 
    - 
     
      
     
     
       
      
     
 
    - 
     
      
     
     
                      
       //cmd su or err
      
     
 
    - 
     
      
     
     
                      
       if(_IOC_TYPE(cmd) != MEMDEV_IOC_MAGIC)
      
     
 
    - 
     
      
     
     
                              
       return -EINVAL;
      
     
 
    - 
     
      
     
     
                      
       if(_IOC_NR(cmd) > MEMDEV_IOC_MAXNR)
      
     
 
    - 
     
      
     
     
                              
       return -EINVAL;
      
     
 
    - 
     
      
     
     
                      
       if (_IOC_DIR(cmd) & _IOC_READ){ 
       if(access_ok(VERIFY_WRITE, (
       void *)arg, _IOC_SIZE(cmd))){ 
       return -EFAULT; } }
      
     
 
    - 
     
      
     
     
                      
       else 
       if(_IOC_DIR(cmd) & _IOC_WRITE) { 
       if (access_ok(VERIFY_READ, (
       void *)arg, _IOC_SIZE(cmd))){ 
       return -EFAULT; } }
      
     
 
    - 
     
      
     
     
       
      
     
 
    - 
     
      
     
     
                      
       switch(son_id){
      
     
 
    - 
     
      
     
     
       
      
     
 
    - 
     
      
     
     
                              
       case 
       0:
      
     
 
    - 
     
      
     
     
                                      
       switch(cmd){
      
     
 
    - 
     
      
     
     
       
      
     
 
    - 
     
      
     
     
                                              
       case MEMDEV_IOCPRINT:   
       //Print information
      
     
 
    - 
     
      
     
     
      
                                                       printk(
       "TQ210_LED1 demo to stephen zhou\n");
      
     
 
    - 
     
      
     
     
                                              
       break;
      
     
 
    - 
     
      
     
     
       
      
     
 
    - 
     
      
     
     
                                              
       case MEMDEV_IOCGETDATA: 
       //Get parameters
      
     
 
    - 
     
      
     
     
                                                      
       return __put_user(par,(
       int*) arg);
      
     
 
    - 
     
      
     
     
                                              
       break;
      
     
 
    - 
     
      
     
     
       
      
     
 
    - 
     
      
     
     
                                              
       case MEMDEV_IOCSETDATA: 
       //Set parameters
      
     
 
    - 
     
      
     
     
                                                      
       return __get_user(par,(
       int*) arg);
      
     
 
    - 
     
      
     
     
                                              
       break;
      
     
 
    - 
     
      
     
     
       
      
     
 
    - 
     
      
     
     
                                              
       default:
      
     
 
    - 
     
      
     
     
                                                      
       return -EINVAL;
      
     
 
    - 
     
      
     
     
                                              
       break;
      
     
 
    - 
     
      
     
     
      
                                       }
      
     
 
    - 
     
      
     
     
                              
       break;
      
     
 
    - 
     
      
     
     
       
      
     
 
    - 
     
      
     
     
                              
       case 
       1:
      
     
 
    - 
     
      
     
     
                                      
       switch(cmd){
      
     
 
    - 
     
      
     
     
       
      
     
 
    - 
     
      
     
     
                                              
       case MEMDEV_IOCPRINT:   
       //Print information
      
     
 
    - 
     
      
     
     
      
                                                       printk(
       "TQ210_LED2 demo to stephen zhou\n");
      
     
 
    - 
     
      
     
     
                                              
       break;
      
     
 
    - 
     
      
     
     
       
      
     
 
    - 
     
      
     
     
                                              
       case MEMDEV_IOCGETDATA: 
       //Get parameters
      
     
 
    - 
     
      
     
     
                                                      
       return __put_user(par,(
       int*) arg);
      
     
 
    - 
     
      
     
     
                                              
       break;
      
     
 
    - 
     
      
     
     
       
      
     
 
    - 
     
      
     
     
                                              
       case MEMDEV_IOCSETDATA: 
       //Set parameters
      
     
 
    - 
     
      
     
     
                                                      
       return __get_user(par,(
       int*) arg);
      
     
 
    - 
     
      
     
     
                                              
       break;
      
     
 
    - 
     
      
     
     
       
      
     
 
    - 
     
      
     
     
                                              
       default:
      
     
 
    - 
     
      
     
     
                                                      
       return -EINVAL;
      
     
 
    - 
     
      
     
     
                                              
       break;
      
     
 
    - 
     
      
     
     
      
                                       }
      
     
 
    - 
     
      
     
     
                              
       break;
      
     
 
    - 
     
      
     
     
      
                       }
      
     
 
    - 
     
      
     
     
       
      
     
 
    - 
     
      
     
     
      
                       printk(
       "TQ210_LED[SUCCESS]:ioctl\n");
      
     
 
    - 
     
      
     
     
                      
       return 
       0;
      
     
 
    - 
     
      
     
     
      
       }
      
     
 
   
 
好了,这里open、write、read、ioctl都已经实现啦,那么我们在写一个简单的app测试一下吧
四、编写测试APP
这里我写了一个闪烁的app代码
在src/app目录下新建一个TQ210_app_led.c的文件,用于用户态的程序
包含基本头文件
   
    - 
     
      
     
     
      
       #include <sys/types.h>
      
     
 
    - 
     
      
     
     
      
       #include <sys/stat.h>
      
     
 
    - 
     
      
     
     
      
       #include <fcntl.h>
      
     
 
    - 
     
      
     
     
      
       #include <stdio.h>
      
     
 
    - 
     
      
     
     
      
       #include <unistd.h>
      
     
 
    - 
     
      
     
     
      
       #include <string.h>
      
     
 
    - 
     
      
     
     
      
       #include <sys/ioctl.h>
      
     
 
    - 
     
      
     
     
      
       #include <stdlib.h>
      
     
 
    - 
     
      
     
     
      
       #include "../../../include/TQ210_LED/device/TQ210_device_led.h"
      
     
 
   
 
这里我写了一个基本的打印函数
   
    - 
     
      
     
     
      
       void print(char* led1_state,char* led2_state){
      
     
 
    - 
     
      
     
     
      
               system(
       "clear");
      
     
 
    - 
     
      
     
     
              
       printf(
       "/*****************************************************************************\n");
      
     
 
    - 
     
      
     
     
              
       printf(
       "* *\n");
      
     
 
    - 
     
      
     
     
              
       printf(
       "* TQ210 LED的demo演示 *\n");
      
     
 
    - 
     
      
     
     
              
       printf(
       "* Copyright (C) 2021 StephenZhou *\n");
      
     
 
    - 
     
      
     
     
              
       printf(
       "* *\n");
      
     
 
    - 
     
      
     
     
              
       printf(
       "*----------------------------------------------------------------------------*\n");
      
     
 
    - 
     
      
     
     
              
       printf(
       "* Device : State *\n");
      
     
 
    - 
     
      
     
     
              
       printf(
       "*----------------------------------------------------------------------------*\n");
      
     
 
    - 
     
      
     
     
              
       if(
       strcmp(led1_state,
       "ON") == 
       0){ 
       printf(
       "* TQ210_LED_ONE : ON *\n"); }
      
     
 
    - 
     
      
     
     
              
       else{ 
       printf(
       "* TQ210_LED_ONE : OFF *\n"); }
      
     
 
    - 
     
      
     
     
              
       printf(
       "*----------------------------------------------------------------------------*\n");
      
     
 
    - 
     
      
     
     
              
       if(
       strcmp(led2_state,
       "ON") == 
       0){ 
       printf(
       "* TQ210_LED_TWO : ON *\n"); }
      
     
 
    - 
     
      
     
     
              
       else{ 
       printf(
       "* TQ210_LED_TWO : OFF *\n"); }
      
     
 
    - 
     
      
     
     
              
       printf(
       "*----------------------------------------------------------------------------*\n");
      
     
 
    - 
     
      
     
     
              
       printf(
       "* Change History : *\n");
      
     
 
    - 
     
      
     
     
              
       printf(
       "* <Date> | <Version> | <Author> | <Description> *\n");
      
     
 
    - 
     
      
     
     
              
       printf(
       "*----------------------------------------------------------------------------*\n");
      
     
 
    - 
     
      
     
     
              
       printf(
       "* 2020/5/25 | 1.0.0.0 | StephenZhou | LED Demo *\n");
      
     
 
    - 
     
      
     
     
              
       printf(
       "*----------------------------------------------------------------------------*\n");
      
     
 
    - 
     
      
     
     
              
       printf(
       "* *\n");
      
     
 
    - 
     
      
     
     
              
       printf(
       "*****************************************************************************/\n");
      
     
 
    - 
     
      
     
     
       
      
     
 
    - 
     
      
     
     
      
       }
      
     
 
   
 
第一步就是声明基本变量,然后就按照open、write的方式写就可以了,注释我写的非常清楚
就是先open打开,然后read读取状态,把状态打印出来,并且调用ioctl来打印基本信息,在使用write写入状态。
   
    - 
     
      
     
     
      
       int main(int argc,char **argv){
      
     
 
    - 
     
      
     
     
       
      
     
 
    - 
     
      
     
     
              
       /* Two LEDs flash each other and print the status */
      
     
 
    - 
     
      
     
     
              
       int cmd = 
       0,arg = 
       0,val = 
       0;
      
     
 
    - 
     
      
     
     
              
       char led1_state[
       4] = {
       0},led2_state[
       4] = {
       0};
      
     
 
    - 
     
      
     
     
              
       //1. open udev device
      
     
 
    - 
     
      
     
     
              
       int led_fd1 = open(LED_DEV_ONE_NAME,O_RDWR);
      
     
 
    - 
     
      
     
     
              
       int led_fd2 = open(LED_DEV_TWO_NAME,O_RDWR);
      
     
 
    - 
     
      
     
     
              
       if(led_fd1 == 
       -1 || led_fd2 == 
       -1){
      
     
 
    - 
     
      
     
     
                      
       printf(
       "error:can't open led device\n");
      
     
 
    - 
     
      
     
     
                      
       return 
       -1;
      
     
 
    - 
     
      
     
     
      
               }
      
     
 
    - 
     
      
     
     
       
      
     
 
    - 
     
      
     
     
              
       //2. print msg to kernel ioctl
      
     
 
    - 
     
      
     
     
      
               cmd = MEMDEV_IOCPRINT;
      
     
 
    - 
     
      
     
     
              
       if(ioctl(led_fd1,cmd,&arg) == 
       -1) { 
       printf(
       "error:can't ioctl for led1\n"); 
       return 
       -1; }
      
     
 
    - 
     
      
     
     
      
               cmd = MEMDEV_IOCPRINT;
      
     
 
    - 
     
      
     
     
              
       if(ioctl(led_fd2,cmd,&arg) == 
       -1) { 
       printf(
       "error:can't ioctl for led2\n"); 
       return 
       -1; }
      
     
 
    - 
     
      
     
     
       
      
     
 
    - 
     
      
     
     
              
       //3. wink
      
     
 
    - 
     
      
     
     
              
       while(
       1){
      
     
 
    - 
     
      
     
     
       
      
     
 
    - 
     
      
     
     
                      
       //led one wink led two close
      
     
 
    - 
     
      
     
     
      
                       val = 
       1;
      
     
 
    - 
     
      
     
     
                      
       if(write(led_fd1,&val,
       sizeof(val)) == 
       -1){ 
       printf(
       "error:can't write for led1\n"); 
       return 
       -1; }
      
     
 
    - 
     
      
     
     
      
                       val = 
       0;
      
     
 
    - 
     
      
     
     
                      
       if(write(led_fd2,&val,
       sizeof(val)) == 
       -1){ 
       printf(
       "error:can't write for led2\n"); 
       return 
       -1; }
      
     
 
    - 
     
      
     
     
       
      
     
 
    - 
     
      
     
     
                      
       //clear string
      
     
 
    - 
     
      
     
     
                      
       memset(led1_state,
       0,
       sizeof(led1_state));
      
     
 
    - 
     
      
     
     
                      
       memset(led2_state,
       0,
       sizeof(led2_state));
      
     
 
    - 
     
      
     
     
       
      
     
 
    - 
     
      
     
     
                      
       //read led state
      
     
 
    - 
     
      
     
     
                      
       if(read(led_fd1,led1_state,
       sizeof(led1_state)) == 
       -1){ 
       printf(
       "error:cant't read for led1\n"); 
       return 
       -1; }
      
     
 
    - 
     
      
     
     
                      
       if(read(led_fd2,led2_state,
       sizeof(led2_state)) == 
       -1){ 
       printf(
       "error:cant't read for led2\n"); 
       return 
       -1; }
      
     
 
    - 
     
      
     
     
      
                       print(led1_state,led2_state);
      
     
 
    - 
     
      
     
     
       
      
     
 
    - 
     
      
     
     
      
                       sleep(
       3);
      
     
 
    - 
     
      
     
     
       
      
     
 
    - 
     
      
     
     
                      
       //led two wink led one close
      
     
 
    - 
     
      
     
     
      
                       val = 
       1;
      
     
 
    - 
     
      
     
     
                      
       if(write(led_fd2,&val,
       sizeof(val)) == 
       -1){ 
       printf(
       "error:can't write for led1\n"); 
       return 
       -1; }
      
     
 
    - 
     
      
     
     
      
                       val = 
       0;
      
     
 
    - 
     
      
     
     
                      
       if(write(led_fd1,&val,
       sizeof(val)) == 
       -1){ 
       printf(
       "error:can't write for led2\n"); 
       return 
       -1; }
      
     
 
    - 
     
      
     
     
       
      
     
 
    - 
     
      
     
     
                      
       //clear string
      
     
 
    - 
     
      
     
     
                      
       memset(led1_state,
       0,
       sizeof(led1_state));
      
     
 
    - 
     
      
     
     
                      
       memset(led2_state,
       0,
       sizeof(led2_state));
      
     
 
    - 
     
      
     
     
       
      
     
 
    - 
     
      
     
     
                      
       //read led state
      
     
 
    - 
     
      
     
     
                      
       if(read(led_fd1,led1_state,
       sizeof(led1_state)) == 
       -1){ 
       printf(
       "error:cant't read for led1\n"); 
       return 
       -1; }
      
     
 
    - 
     
      
     
     
                      
       if(read(led_fd2,led2_state,
       sizeof(led2_state)) == 
       -1){ 
       printf(
       "error:cant't read for led2\n"); 
       return 
       -1; }
      
     
 
    - 
     
      
     
     
      
                       print(led1_state,led2_state);
      
     
 
    - 
     
      
     
     
       
      
     
 
    - 
     
      
     
     
      
                       sleep(
       3);
      
     
 
    - 
     
      
     
     
       
      
     
 
    - 
     
      
     
     
      
               }
      
     
 
    - 
     
      
     
     
       
      
     
 
    - 
     
      
     
     
              
       return 
       0;
      
     
 
    - 
     
      
     
     
      
       }                                                                                                                                                                                                                                                  
      
     
 
   
 
我们在把app文件添加到make里,用交叉编译器
以下是我修改后的make
我使用了一些mv命令来把生成的临时文件以及目标文件都放到固定目录中。
   
    - 
     
      
     
     
      
       ifneq ($(KERNELRELEASE),)
      
     
 
    - 
     
      
     
     
      
               obj-m  := ./src/TQ210_LED/device/TQ210_device_led.o
      
     
 
    - 
     
      
     
     
      
       else
      
     
 
    - 
     
      
     
     
      
               KDIR := /home/beis/TQ/opt/EmbedSky/TQ210/Kernel_3.0.8_TQ210_for_Linux_v2.4
      
     
 
    - 
     
      
     
     
      
       all:
      
     
 
    - 
     
      
     
     
      
               $(MAKE) -C $(KDIR) M=$(PWD) modules CROSS_COMPILE=arm-embedsky-linux-gnueabi-
      
     
 
    - 
     
      
     
     
      
               arm-embedsky-linux-gnueabi-gcc ./src/TQ210_LED/app/TQ210_app_led.c -o TQ210_app_led
      
     
 
    - 
     
      
     
     
      
               mv ./src/TQ210_LED/device/*.o                   ./output
      
     
 
    - 
     
      
     
     
      
               mv *.symvers                                    ./output
      
     
 
    - 
     
      
     
     
      
               mv *.order                                      ./output
      
     
 
    - 
     
      
     
     
      
               mv ./src/TQ210_LED/device/*.mod.c               ./output
      
     
 
    - 
     
      
     
     
      
               mv ./src/TQ210_LED/device/*.ko                  ./arch/arm/TQ210_LED
      
     
 
    - 
     
      
     
     
      
               mv TQ210_app_led                                ./arch/arm/TQ210_LED
      
     
 
    - 
     
      
     
     
      
       clean:
      
     
 
    - 
     
      
     
     
      
               $(MAKE) -C $(KDIR) M=$(PWD) clean
      
     
 
    - 
     
      
     
     
      
               rm ./output/*
      
     
 
    - 
     
      
     
     
      
               rm ./arch/arm/TQ210_LED/*
      
     
 
    - 
     
      
     
     
      
               rm ./arch/arm/network/*
      
     
 
    - 
     
      
     
     
      
       endif
      
     
 
   
 
这个时候你在make一下就能在arch/arm/TQ210_LED目录下看到app和.ko文件了。
   
    - 
     
      
     
     
      
       beis@ubuntu:~/moudul/arch/arm/TQ210_LED$ tree
      
     
 
    - 
     
      
     
     
      
       .
      
     
 
    - 
     
      
     
     
      
       ├── TQ210_app_led
      
     
 
    - 
     
      
     
     
      
       └── TQ210_device_led.ko
      
     
 
    - 
     
      
     
     
       
      
     
 
    - 
     
      
     
     
      
       0 directories, 2 files
      
     
 
   
 
最后别忘记git一下
   
    - 
     
      
     
     
      
       git add .
      
     
 
    - 
     
      
     
     
      
       git commit -m 
       "two"
      
     
 
   
 
到这一步基本一个demo就已经写完了,那么接下里我们让它物联网
写一个server服务器来控制它
以下代码是我以前写过web ui的方法实现的。
五. 物联网
在src目录下新建一个network目录,在此目录新建一个server.c文件,同理include目录也是一样
这部分如果你对http开发以及网站开发没有开发经验的话可以跳过,这里我是自己写的服务器是为了让大家更清楚了解物联网原理,可以去看下我关于http协议的讲解,以及我开源的http解析代码
   
    - 
     
      
     
     
      
       mkdir src/network
      
     
 
    - 
     
      
     
     
      
       touch src/network/server.c
      
     
 
    - 
     
      
     
     
      
       mkdir include/network
      
     
 
    - 
     
      
     
     
      
       touch include/network/server.h
      
     
 
   
 
我们在写一个html用来展示前端,其就post的提交
代码很简单,存放在src/network目录下
先在.h文件里把协议以及头文件定义出来
   
    - 
     
      
     
     
      
       #include <stdio.h>
      
     
 
    - 
     
      
     
     
      
       #include <strings.h>
      
     
 
    - 
     
      
     
     
      
       #include <unistd.h>
      
     
 
    - 
     
      
     
     
      
       #include <sys/stat.h>
      
     
 
    - 
     
      
     
     
      
       #include <fcntl.h>
      
     
 
    - 
     
      
     
     
      
       #include <sys/types.h>
      
     
 
    - 
     
      
     
     
      
       #include <sys/socket.h>
      
     
 
    - 
     
      
     
     
      
       #include <netinet/in.h>
      
     
 
    - 
     
      
     
     
      
       #include <arpa/inet.h>
      
     
 
    - 
     
      
     
     
      
       #include <string.h>
      
     
 
    - 
     
      
     
     
      
       #include "../../include/TQ210_LED/device/TQ210_device_led.h"
      
     
 
    - 
     
      
     
     
       
      
     
 
    - 
     
      
     
     
      
       //server config
      
     
 
    - 
     
      
     
     
      
       #define PORT 8081
      
     
 
    - 
     
      
     
     
      
       #define BACKLOG 10
      
     
 
    - 
     
      
     
     
       
      
     
 
    - 
     
      
     
     
      
       //http
      
     
 
    - 
     
      
     
     
      
       #define STATE_OK "HTTP/1.0 200 OK\r\n"
      
     
 
    - 
     
      
     
     
      
       #define SERVER_TYPE "Server: DWBServer\r\nContent-Type: text/html;charset=utf-8\r\n\r\n"
      
     
 
    - 
     
      
     
     
       
      
     
 
    - 
     
      
     
     
      
       //file
      
     
 
    - 
     
      
     
     
      
       #define INDEX_NAME "./index.html"
      
     
 
    - 
     
      
     
     
       
      
     
 
    - 
     
      
     
     
      
       //http ops
      
     
 
    - 
     
      
     
     
      
       #define STATE_GET_INDEX 1
      
     
 
    - 
     
      
     
     
      
       #define STATE_BUTTON_FOO 2
      
     
 
    - 
     
      
     
     
      
       #define STATE_200 3
      
     
 
    - 
     
      
     
     
       
      
     
 
    - 
     
      
     
     
      
       //led
      
     
 
    - 
     
      
     
     
      
       #define LED_1 1
      
     
 
    - 
     
      
     
     
      
       #define LED_2 2
      
     
 
    - 
     
      
     
     
      
       #define LED_UP 3
      
     
 
    - 
     
      
     
     
      
       #define LED_DOWN 4
      
     
 
   
 
思路就是post提交给服务器,服务器拿到post数据然后判断数据信息来执行对应的功能
这里使用了form的表格实现了这一功能
   
    - 
     
      
     
     
      
       <html>
      
     
 
    - 
     
      
     
     
      
       <body>
      
     
 
    - 
     
      
     
     
      
       <form action = "" method = "post">
      
     
 
    - 
     
      
     
     
                       
       <button name="foo" value="LED1_Light">LED1_点亮
       </button>
      
     
 
    - 
     
      
     
     
      
       </form>
      
     
 
    - 
     
      
     
     
      
       <form action = "" method = "post">
      
     
 
    - 
     
      
     
     
             
       <button name="foo" value="LED1_Ext">LED1_熄灯
       </button>
      
     
 
    - 
     
      
     
     
      
       </form>
      
     
 
    - 
     
      
     
     
      
       <form action = "" method = "post">
      
     
 
    - 
     
      
     
     
                       
       <button name="foo" value="LED2_Light">LED2_点亮
       </button>
      
     
 
    - 
     
      
     
     
      
       </form>
      
     
 
    - 
     
      
     
     
      
       <form action = "" method = "post">
      
     
 
    - 
     
      
     
     
             
       <button name="foo" value="LED2_Ext">LED2_熄灯
       </button>
      
     
 
    - 
     
      
     
     
      
       </form>
      
     
 
    - 
     
      
     
     
      
       </body>
      
     
 
    - 
     
      
     
     
      
       </html>
      
     
 
   
 
server.c部分
首先包含头文件:
#include "../../include/network/server.h" 
在写一个解析http协议的head代码:
这一部分是用来获取报文头的,便于服务器判断请求参数是什么
   
    - 
     
      
     
     
      
       int GetHead(char* buff,char* t){
      
     
 
    - 
     
      
     
     
       
      
     
 
    - 
     
      
     
     
              
       if(buff == 
       NULL || t == 
       NULL){
      
     
 
    - 
     
      
     
     
                      
       return 
       -1;
      
     
 
    - 
     
      
     
     
      
               }
      
     
 
    - 
     
      
     
     
              
       int str_len = 
       strlen(buff);
      
     
 
    - 
     
      
     
     
              
       int i = 
       0;
      
     
 
    - 
     
      
     
     
              
       for(; i<str_len ;++i){
      
     
 
    - 
     
      
     
     
       
      
     
 
    - 
     
      
     
     
                      
       if(buff[i] == 
       '\r'){
      
     
 
    - 
     
      
     
     
                              
       break;
      
     
 
    - 
     
      
     
     
      
                       }
      
     
 
    - 
     
      
     
     
       
      
     
 
    - 
     
      
     
     
      
                       t[i] = buff[i];
      
     
 
    - 
     
      
     
     
       
      
     
 
    - 
     
      
     
     
      
               }
      
     
 
    - 
     
      
     
     
       
      
     
 
    - 
     
      
     
     
              
       return 
       0;
      
     
 
    - 
     
      
     
     
       
      
     
 
    - 
     
      
     
     
      
       }
      
     
 
   
 
在写一个函数用来获取报文尾部,因为post提交的话文本会在报文体的尾部
   
    - 
     
      
     
     
      
       int GetEnd(char* buff,char* t){
      
     
 
    - 
     
      
     
     
              
       if(buff == 
       NULL || t == 
       NULL){
      
     
 
    - 
     
      
     
     
                      
       return 
       -1;
      
     
 
    - 
     
      
     
     
      
               }
      
     
 
    - 
     
      
     
     
       
      
     
 
    - 
     
      
     
     
              
       int i = 
       strlen(buff);
      
     
 
    - 
     
      
     
     
              
       int count = 
       0;
      
     
 
    - 
     
      
     
     
              
       int d = 
       0;
      
     
 
    - 
     
      
     
     
              
       int y = 
       0;
      
     
 
    - 
     
      
     
     
              
       for(;i>
       0;--i){
      
     
 
    - 
     
      
     
     
                      
       if(buff[i] == 
       '\n'){
      
     
 
    - 
     
      
     
     
                              
       break;
      
     
 
    - 
     
      
     
     
      
                       }
      
     
 
    - 
     
      
     
     
      
                       ++count;
      
     
 
    - 
     
      
     
     
      
               }
      
     
 
    - 
     
      
     
     
       
      
     
 
    - 
     
      
     
     
      
               d = 
       strlen(buff) - count+
       1;
      
     
 
    - 
     
      
     
     
              
       for(;buff[d] != 
       '\0';++d){
      
     
 
    - 
     
      
     
     
      
                       t[y++] = buff[d];
      
     
 
    - 
     
      
     
     
       
      
     
 
    - 
     
      
     
     
      
               }
      
     
 
    - 
     
      
     
     
       
      
     
 
    - 
     
      
     
     
              
       return 
       0;
      
     
 
    - 
     
      
     
     
      
       }
      
     
 
   
 
然后在写一个获取HTTP状态的函数,这一部分主要利用解析到的文本头来判断http客户端执行了什么操作,然后返回给我们,便于我们做对应的操作
   
    - 
     
      
     
     
      
       int GET_HTTP_STATE(char* buff){
      
     
 
    - 
     
      
     
     
              
      
     
 
    - 
     
      
     
     
              
       if(buff == 
       NULL){  
      
     
 
    - 
     
      
     
     
                      
       return 
       0;
      
     
 
    - 
     
      
     
     
      
               }
      
     
 
    - 
     
      
     
     
       
      
     
 
    - 
     
      
     
     
              
       char Head[
       256] = {
       0};
      
     
 
    - 
     
      
     
     
      
               GetHead(buff,Head);
      
     
 
    - 
     
      
     
     
              
       if(Head == 
       NULL){ 
       return 
       0; }
      
     
 
    - 
     
      
     
     
              
      
     
 
    - 
     
      
     
     
              
       if(
       strcmp(Head,
       "GET / HTTP/1.1") == 
       0){
      
     
 
    - 
     
      
     
     
                      
      
     
 
    - 
     
      
     
     
                      
       return STATE_GET_INDEX;
      
     
 
    - 
     
      
     
     
          
      
     
 
    - 
     
      
     
     
      
               }
      
     
 
    - 
     
      
     
     
          
      
     
 
    - 
     
      
     
     
              
       char ff[
       256] = {
       0};
      
     
 
    - 
     
      
     
     
              
       if(
       strcmp(Head,
       "POST / HTTP/1.1") == 
       0){
      
     
 
    - 
     
      
     
     
                                
      
     
 
    - 
     
      
     
     
                      
       return STATE_BUTTON_FOO;
      
     
 
    - 
     
      
     
     
       
      
     
 
    - 
     
      
     
     
      
               }
      
     
 
    - 
     
      
     
     
       
      
     
 
    - 
     
      
     
     
              
       return STATE_200;
      
     
 
    - 
     
      
     
     
       
      
     
 
    - 
     
      
     
     
      
       }
      
     
 
   
 
然后就是led操作的函数,这个就不用多说了,非常简单
   
    - 
     
      
     
     
      
       int Led_Ops(int LED_INDEX,int STATE){
      
     
 
    - 
     
      
     
     
       
      
     
 
    - 
     
      
     
     
              
       int fd  = 
       0;
      
     
 
    - 
     
      
     
     
              
       int val = 
       0;
      
     
 
    - 
     
      
     
     
       
      
     
 
    - 
     
      
     
     
              
       if(LED_INDEX == LED_1){
      
     
 
    - 
     
      
     
     
       
      
     
 
    - 
     
      
     
     
      
                       fd = open(LED_DEV_ONE_NAME,O_RDWR);
      
     
 
    - 
     
      
     
     
                      
       if(fd == 
       -1) { 
       return 
       -1; }
      
     
 
    - 
     
      
     
     
      
               }
      
     
 
    - 
     
      
     
     
              
      
     
 
    - 
     
      
     
     
              
       if(LED_INDEX == LED_2){
      
     
 
    - 
     
      
     
     
      
                       fd = open(LED_DEV_TWO_NAME,O_RDWR);
      
     
 
    - 
     
      
     
     
                      
       if(fd == 
       -1) { 
       return 
       -1; }
      
     
 
    - 
     
      
     
     
      
               }
      
     
 
    - 
     
      
     
     
       
      
     
 
    - 
     
      
     
     
              
       if(STATE == LED_UP){
      
     
 
    - 
     
      
     
     
      
                       val = 
       1;
      
     
 
    - 
     
      
     
     
      
                       write(fd,&val,
       sizeof(val));
      
     
 
    - 
     
      
     
     
      
               }
      
     
 
    - 
     
      
     
     
       
      
     
 
    - 
     
      
     
     
              
       if(STATE == LED_DOWN){
      
     
 
    - 
     
      
     
     
      
                       val = 
       0;
      
     
 
    - 
     
      
     
     
      
                       write(fd,&val,
       sizeof(val));
      
     
 
    - 
     
      
     
     
      
               }
      
     
 
    - 
     
      
     
     
       
      
     
 
    - 
     
      
     
     
      
               close(fd);
      
     
 
    - 
     
      
     
     
       
      
     
 
    - 
     
      
     
     
      
       }
      
     
 
   
 
最后就是exec的事件函数,根据返回的状态执行对应的操作,最后也要给http客户端进行反馈,200告诉它我们完成了工作,其次post提交之后我们也要返回页面,因为http客户端会显示服务器返回的数据。
   
    - 
     
      
     
     
      
       int Http_Exec(int state,int fd,char* buff){
      
     
 
    - 
     
      
     
     
       
      
     
 
    - 
     
      
     
     
              
       char rt[
       2*
       1024] = {
       0};
      
     
 
    - 
     
      
     
     
              
       if(state == STATE_GET_INDEX){
      
     
 
    - 
     
      
     
     
                      
       strcat(rt,STATE_OK);
      
     
 
    - 
     
      
     
     
                      
       strcat(rt,SERVER_TYPE);
      
     
 
    - 
     
      
     
     
                      
       strcat(rt,index_body);
      
     
 
    - 
     
      
     
     
      
                       send(fd,rt,
       strlen(rt),
       0);
      
     
 
    - 
     
      
     
     
      
               }
      
     
 
    - 
     
      
     
     
       
      
     
 
    - 
     
      
     
     
              
       if(state == STATE_BUTTON_FOO){
      
     
 
    - 
     
      
     
     
                      
       char fun[
       256] = {
       0};
      
     
 
    - 
     
      
     
     
      
                       GetEnd(buff,fun);
      
     
 
    - 
     
      
     
     
                      
       strcat(rt,STATE_OK);
      
     
 
    - 
     
      
     
     
                      
       strcat(rt,SERVER_TYPE);
      
     
 
    - 
     
      
     
     
                      
       strcat(rt,index_body);
      
     
 
    - 
     
      
     
     
      
                       send(fd,rt,
       strlen(rt),
       0);
      
     
 
    - 
     
      
     
     
                      
       if(
       strcmp(fun,
       "foo=LED1_Light") == 
       0){
      
     
 
    - 
     
      
     
     
      
                               Led_Ops(LED_1,LED_UP);
      
     
 
    - 
     
      
     
     
      
                       }
      
     
 
    - 
     
      
     
     
                      
       if(
       strcmp(fun,
       "foo=LED1_Ext") == 
       0){
      
     
 
    - 
     
      
     
     
      
                               Led_Ops(LED_1,LED_DOWN);
      
     
 
    - 
     
      
     
     
      
                       }
      
     
 
    - 
     
      
     
     
                      
       if(
       strcmp(fun,
       "foo=LED2_Light") == 
       0){
      
     
 
    - 
     
      
     
     
      
                               Led_Ops(LED_2,LED_UP);
      
     
 
    - 
     
      
     
     
      
                       }
      
     
 
    - 
     
      
     
     
                      
       if(
       strcmp(fun,
       "foo=LED2_Ext") == 
       0){
      
     
 
    - 
     
      
     
     
      
                               Led_Ops(LED_2,LED_DOWN);
      
     
 
    - 
     
      
     
     
      
                       }
      
     
 
    - 
     
      
     
     
       
      
     
 
    - 
     
      
     
     
      
               }
      
     
 
    - 
     
      
     
     
       
      
     
 
    - 
     
      
     
     
              
       if(state == STATE_200){
      
     
 
    - 
     
      
     
     
                      
       strcat(rt,STATE_OK);
      
     
 
    - 
     
      
     
     
                      
       strcat(rt,SERVER_TYPE);
      
     
 
    - 
     
      
     
     
      
                       send(fd,rt,
       strlen(rt),
       0);
      
     
 
    - 
     
      
     
     
      
               }
      
     
 
    - 
     
      
     
     
       
      
     
 
    - 
     
      
     
     
              
       return 
       0;
      
     
 
    - 
     
      
     
     
      
       }
      
     
 
   
 
然后就是main函数
先初始化tcp,这里我写的不是多线程,是单线程的,也就是说同一时间只能有一个http客户端响应。
   
    - 
     
      
     
     
       
       int listenfd, connectfd;
      
     
 
    - 
     
      
     
     
              
       struct sockaddr_in server, client;
      
     
 
    - 
     
      
     
     
              
       socklen_t addrlen;
      
     
 
    - 
     
      
     
     
       
      
     
 
    - 
     
      
     
     
              
       if((listenfd = socket(AF_INET, SOCK_STREAM, 
       0)) == 
       -1){
      
     
 
    - 
     
      
     
     
      
                       perror(
       "[SERVER_ERROR] socket\n");
      
     
 
    - 
     
      
     
     
                      
       return 
       -1;
      
     
 
    - 
     
      
     
     
      
               }
      
     
 
    - 
     
      
     
     
       
      
     
 
    - 
     
      
     
     
              
       int opt = 
       1;
      
     
 
    - 
     
      
     
     
      
               setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &opt, 
       sizeof(opt));
      
     
 
    - 
     
      
     
     
      
               bzero(&server, 
       sizeof(server));
      
     
 
    - 
     
      
     
     
      
               server.sin_family = AF_INET;
      
     
 
    - 
     
      
     
     
      
               server.sin_port = htons(PORT);
      
     
 
    - 
     
      
     
     
      
               server.sin_addr.s_addr = htonl(INADDR_ANY);
      
     
 
    - 
     
      
     
     
       
      
     
 
    - 
     
      
     
     
              
       if(bind(listenfd, (struct sockaddr *)&server, 
       sizeof(server)) == 
       -1){
      
     
 
    - 
     
      
     
     
      
                       perror(
       "[SERVER_ERROR] bind\n");
      
     
 
    - 
     
      
     
     
                      
       return 
       -1;
      
     
 
    - 
     
      
     
     
      
               }
      
     
 
    - 
     
      
     
     
       
      
     
 
    - 
     
      
     
     
      
               addrlen = 
       sizeof(client);
      
     
 
    - 
     
      
     
     
       
      
     
 
    - 
     
      
     
     
      
               FILE* fp = fopen(INDEX_NAME,
       "r");
      
     
 
    - 
     
      
     
     
              
       if(fp == 
       NULL){
      
     
 
    - 
     
      
     
     
      
                       perror(
       "[SERVER_ERROR] index.html file\n");
      
     
 
    - 
     
      
     
     
                      
       return 
       -1;
      
     
 
    - 
     
      
     
     
      
               }
      
     
 
    - 
     
      
     
     
       
      
     
 
   
 
把index.html读取出来,返回给客户端
fread(index_body,sizeof(index_body),1,fp); 
然后while循环监听并执行事件
   
    - 
     
      
     
     
       
       while(
       1){
      
     
 
    - 
     
      
     
     
       
      
     
 
    - 
     
      
     
     
                      
       if(listen(listenfd, BACKLOG) == 
       -1){
      
     
 
    - 
     
      
     
     
      
                               perror(
       "[SERVER_ERROR] listen\n");
      
     
 
    - 
     
      
     
     
                              
       return 
       -1;
      
     
 
    - 
     
      
     
     
      
                       }
      
     
 
    - 
     
      
     
     
       
      
     
 
    - 
     
      
     
     
                      
       if((connectfd = accept(listenfd, (struct sockaddr *)&client, &addrlen)) == 
       -1){
      
     
 
    - 
     
      
     
     
      
                               perror(
       "[SERVER_ERROR] accept\n");
      
     
 
    - 
     
      
     
     
                              
       return 
       -1;
      
     
 
    - 
     
      
     
     
      
                       }
      
     
 
    - 
     
      
     
     
       
      
     
 
    - 
     
      
     
     
                      
       char buff[
       1024] = {
       0};
      
     
 
    - 
     
      
     
     
      
                       recv(connectfd,buff,
       1024,
       0);
      
     
 
    - 
     
      
     
     
                      
       //printf("%s\n",buff);
      
     
 
    - 
     
      
     
     
      
                       Http_Exec(GET_HTTP_STATE(buff),connectfd,buff);
      
     
 
    - 
     
      
     
     
      
                       close(connectfd);
      
     
 
    - 
     
      
     
     
      
               }
      
     
 
   
 
最后的关闭tcpfd
   
    - 
     
      
     
     
      
        close(listenfd);
      
     
 
    - 
     
      
     
     
       
       return 
       0;
      
     
 
   
 
六.在make添加支持
   
    - 
     
      
     
     
      
       arm-embedsky-linux-gnueabi-gcc ./src/network/server.c -o server
      
     
 
    - 
     
      
     
     
      
       mv server                                       ./arch/arm/network
      
     
 
    - 
     
      
     
     
      
       cp ./src/network/index.html                     ./arch/arm/network
      
     
 
   
 
好了到这一步就彻底写完啦。
完整的make:
   
    - 
     
      
     
     
      
       ifneq ($(KERNELRELEASE),)
      
     
 
    - 
     
      
     
     
      
               obj-m  := ./src/TQ210_LED/device/TQ210_device_led.o
      
     
 
    - 
     
      
     
     
      
       else
      
     
 
    - 
     
      
     
     
      
               KDIR := /home/beis/TQ/opt/EmbedSky/TQ210/Kernel_3.0.8_TQ210_for_Linux_v2.4
      
     
 
    - 
     
      
     
     
      
       all:
      
     
 
    - 
     
      
     
     
      
               $(MAKE) -C $(KDIR) M=$(PWD) modules CROSS_COMPILE=arm-embedsky-linux-gnueabi-
      
     
 
    - 
     
      
     
     
      
               arm-embedsky-linux-gnueabi-gcc ./src/TQ210_LED/app/TQ210_app_led.c -o TQ210_app_led
      
     
 
    - 
     
      
     
     
      
               arm-embedsky-linux-gnueabi-gcc ./src/network/server.c -o server
      
     
 
    - 
     
      
     
     
      
               mv ./src/TQ210_LED/device/*.o                   ./output
      
     
 
    - 
     
      
     
     
      
               mv *.symvers                                    ./output
      
     
 
    - 
     
      
     
     
      
               mv *.order                                      ./output
      
     
 
    - 
     
      
     
     
      
               mv ./src/TQ210_LED/device/*.mod.c               ./output
      
     
 
    - 
     
      
     
     
      
               mv ./src/TQ210_LED/device/*.ko                  ./arch/arm/TQ210_LED
      
     
 
    - 
     
      
     
     
      
               mv TQ210_app_led                                ./arch/arm/TQ210_LED
      
     
 
    - 
     
      
     
     
      
               mv server                                       ./arch/arm/network
      
     
 
    - 
     
      
     
     
      
               cp ./src/network/index.html                     ./arch/arm/network
      
     
 
    - 
     
      
     
     
      
       clean:
      
     
 
    - 
     
      
     
     
      
               $(MAKE) -C $(KDIR) M=$(PWD) clean
      
     
 
    - 
     
      
     
     
      
               rm ./output/*
      
     
 
    - 
     
      
     
     
      
               rm ./arch/arm/TQ210_LED/*
      
     
 
    - 
     
      
     
     
      
               rm ./arch/arm/network/*
      
     
 
    - 
     
      
     
     
      
       endif
      
     
 
   
 
git保存
   
    - 
     
      
     
     
      
       git add .
      
     
 
    - 
     
      
     
     
      
       git commit -m 
       "three"
      
     
 
   
 
七. 最终演示
使用你的方式在把arch/arm下两个目录的文件全部传输到开发板
我用的是dropbear
然后ssh登入进去执行。
我们是ssh登入进去的,所以很多命令都在/sbin目录下
加载模块:
/sbin/insmod TQ210_device_led.ko 
然后查看一下日志

可以看到成功加载。
然后我们在运行app demo演示:

在看一下server演示:
因是GIF所以比较模糊,大家可以自行测试
这里是github的项目地址:https://github.com/beiszhihao/Internet-of-things-project
日后所有基于物联网的项目我都会开源在这个仓库中
欢迎大家star
转载:https://blog.csdn.net/bjbz_cxy/article/details/117595265