linux驱动、设备、应用之间的关联

硬件层面拥有各个领域功能的外设,这个叫做设备。
与硬件紧密相连的内核,内核中的驱动将设备的硬件抽象为一个个接口,方便上层调用。
应用就是服务用户、实现用户逻辑且位于操作系统至上的计算机程序。

那么操作系统是如何让应用使能设备完成相应的工作呢?驱动又是如何与设备进行绑定的呢?驱动怎样提供系统调用接口给应用呢?

首先要理解的是,linux皆是文件,在linux下写驱动,都是对文件的操作,一个个设备可以抽象为一个个设备文件,当然我们大多数都是写字符设备的驱动,/dev目录下的设备文件也能够被打开,读取,写入以及关闭。例如:/dev/tty0

写驱动实质上就是写系统调用,定制系统调用,为一个设备抽象出更通俗易懂的服务给上层调用,调用者就不需要了解更为硬件的实现过程,字符设备是linux内核抽象出来的一类设备,linux内核为该设备驱动程序提供了一套驱动程序编写框架,驱动程序员编写linux字符设备驱动程序时,必须按照该框架进行。

多说无益,举个栗子,当我们去对一个设备文件进行系统调用的时候,有一个东西是用来标识我们的文件的,就是inode结构体,这个结构体中包含了本设备文件的所有静态信息,而且驱动也是根据这个信息来提供系统调用给到调用者实际操作的可能。也就是说如果你去一个目的地,一辆车来找你,他是需要你的相关信息的,不然他不可能会找到你,也就不可能会提供服务给你,

inode

这是驱动三大数据结构之一,他们分别是inode、file_operation、file
什么是inode呢,操作系统学习时,了解到每个文件都有一个inode,且大小一般是128字节或256字节。inode 是 UNIX 操作系统中的一种数据结构,其本质是结构体,它包含了与文件系统中各个文件相关的一些重要信息。在 UNIX 中创建文件系统时,同时将会创建大量的 inode 。通常,文件系统磁盘空间中大约百分之一空间分配给了 inode 表。
inode定义在include/linux/fs.h文件中
对于任何一个已存在的文件,在内核都有一个称为struct inode的结构体(定义于include/linux/fs.h)与之对应,这是一个结构成员繁多的数据结构,但是对于字符设备而言,真正需要关心的只有以下三个成员:

dev_t    i_rdev;//设备号
struct cdev i_cdev;//内核对于一个字符设备的抽象,正是这个结构体将用户空间看到设备//文件跟内核空间看到的字符设备关联了起来。 
const struct file_operations i_fop;//设备操作方法

dev_t这个成员非常重要,他就是设备号,设备号(dev_t)和主(major)、次(minor)设备号关联,(dev_t 是 32 位的量, 其中12 位用作主编号, 20 位用作次编号dev_t 是 32 位的量, 其中12 位用作主编号, 20 位用作次编号.)

crw-rw----  1 root tty       7,   0  1月  7 14:37 vcs
crw-rw----  1 root tty       7,   1  1月  7 14:37 vcs1
crw-rw----  1 root tty       7,   2  1月  7 14:37 vcs2
crw-rw----  1 root tty       7,   3  1月  7 14:37 vcs3
crw-rw----  1 root tty       7,   4  1月  7 14:37 vcs4
crw-rw----  1 root tty       7,   5  1月  7 14:37 vcs5
crw-rw----  1 root tty       7,   6  1月  7 14:37 vcs6
crw-rw----  1 root tty       7,   7  1月  7 14:37 vcs7

中间的逗号分开的就是主从设备号。

  • MKDEV(major,minor)可以生成设备号(dev_t);
  • MAJOR(dev_t)也可以通过设备号获取主设备号;
  • MINOR(dev_t)也可以获取次设备号;

那么这个设备号就成为了驱动和系统调用之间通信的基本保障。
在建立一个字符驱动时你的驱动需要做的第一件事是获取一个或多个设备编号来使用,在Linux内核里,一些主设备编号是静态分派给最普通的设备的,这些设备列表在内核源码树的Documentation/devices.txt 中列出. 因此, 作为一个驱动编写者, 我们有两个选择: 一是简单地捡一个看来没有用的主设备号, 二是让内核以动态方式分配一个主设备号给你. 只要你是你的驱动的唯一用户就可以捡一个编号用; 一旦你的驱动更广泛的被使用了, 一个随机捡来的主编号将导致冲突和麻烦。因此, 对于新驱动, 我们强烈建议使用动态分配来获取你的主设备编号, 而不是随机选取一个当前空闲的编号。

xiaobaicai@xiaobaicai:/proc$ cat devices
Character devices:
  1 mem
  4 /dev/vc/0
  4 tty
  4 ttyS
  5 /dev/tty
  5 /dev/console
  5 /dev/ptmx
  5 ttyprintk
  6 lp
  7 vcs
 10 misc
 13 input
 14 sound/midi
 14 sound/dmmidi
 21 sg
 29 fb
 89 i2c
 99 ppdev
108 ppp
116 alsa
128 ptm
.......

在/proc/devices文件中存放着系统正在使用的驱动,可以通过查看未使用的主设备编号来静态申请

dev_t devno;
int result;
int major=251;

devno = MKDEV(major, 0);
result = register_chrdev_region (devno, 4, "led");
if (result < 0)
{
	printk(KERN_ERR "LED driver can't use major %d\n", major);
	return -ENODEV;
}
//静态申请函数原型
int register_chrdev_region(dev_t first, unsigned int count, char *name);

或者在不知道设备号使用的情况下,我们可以动态申请

int result;
int dev_major
dev_t devno;

result = alloc_chrdev_region(&devno, 0, 4, "led");
dev_major = MAJOR(devno);
/* Alloc for device major failure */
if (result < 0)
{
	printk(KERN_ERR "led driver allocate device major number failure: %d\n",result);
	return -ENODEV;
}
printk(KERN_ERR "led driver choose device major number: %d\n", dev_major);
//动态申请函数原型
int alloc_chrdev_region(dev_t *dev, unsigned int firstminor, unsigned int count, char *name);

file_operations(文件操作方法)

file_operation对于驱动来说也是非常重要的,常称为fops。
它一样是定义在include/linux/fs.h文件中:

struct file_operations {
	struct module *owner;
	loff_t (*llseek) (struct file *, loff_t, int);
	ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
	ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
	ssize_t (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
	ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
	int (*readdir) (struct file *, void *, filldir_t);
	unsigned int (*poll) (struct file *, struct poll_table_struct *);
	long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
	long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
	int (*mmap) (struct file *, struct vm_area_struct *);
	int (*open) (struct inode *, struct file *);
	int (*flush) (struct file *, fl_owner_t id);
	int (*release) (struct inode *, struct file *);
	int (*fsync) (struct file *, int datasync);
	int (*aio_fsync) (struct kiocb *, int datasync);
	int (*fasync) (int, struct file *, int);
	int (*lock) (struct file *, int, struct file_lock *);
	ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);
	unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned 	long, unsigned long);
	int (*check_flags)(int);
	int (*flock) (struct file *, int, struct file_lock *);
	ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int);
	ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int);
	int (*setlease)(struct file *, long, struct file_lock **);
	long (*fallocate)(struct file *file, int mode, loff_t offset, loff_t len);
};

这里面定义的是很多上层系统调用的原型,那么这个结构体就是为驱动注册相应的系统调用函数的,比如:
static struct file_operations led_fops =
{
.owner = THIS_MODULE,
.open = led_open,
.release = led_release,
.unlocked_ioctl = led_ioctl,
};
.open代表定义了open的系统调用,内部实现函数为led_open
.release代表定义了close系统调用,内部实现函数为led_open
该结构体定义在中,其成员除了owner成员 (其意义及使用与struct cdev中的owner成员一致)外,其余成员均为函数指针。这就是为什么struct file_operations中的结构成员会让熟悉linux应用程序编程的朋友有似曾相识的感觉了,当然了有些名字看起来是一样的但其参数还是有所不同的。该结构抽象了一个字符设备所能拥有的所有能力,而驱动程序员正是通过实现这些函数指针,去实现对于一个设备的控制与操作的。file_operations就是我们注册的字符设备的操作方法,他会被绑定到一些结构体里面,然后注册到内核中,其中有一个很重要的结构体cdev。cdev也存在于inode结构体中。

cdev

内核对于所有字符设备,抽象出来了一个数据结构,因为光一个设备号不足以表示一个设备,该数据结构的一个实体便表示内核中的一个字符设备:

struct cdev { 
	struct kobject kobj; //内嵌的内核对象. 
	struct module owner; //该字符设备所在的内核模块的对象指针. 
	const struct file_operations ops; //该结构描述了字符设备所能实现的方法,是极为关键的一个结构体. 
	struct list_head list; //用来将已经向内核注册的所有字符设备形成链表. 
	dev_t dev; //字符设备的设备号,由主设备号和次设备号构成. 
	unsigned int count; //隶属于同一主设备号的次设备号的个数. 
};

对于cdev的操作:
void cdev_init(struct cdev , const struct file_operations );
该函数主要对struct cdev结构体做初始化,主要工作有(1)将整个结构体清零;(2)初始化list成员使其指向自身;(3)初始化kobj成员;(4)初始化ops成员.

struct cdev *cdev_alloc(void);
该函数主要分配一个struct cdev结构,并做了cdev_init中所做的前面3步初始化工作(第四步初始化工作需要在调用cdev_alloc后,显式的做初始化即: .ops=xxx_ops).

在上面的两个初始化的函数中,我们没有看到关于owner成员、dev成员、count成员的初始化;其实,owner成员的存在体现了驱动程序与内核模块间的亲密关系,struct module是内核对于一个模块的抽象,该成员在字符设备中可以体现该设备隶属于哪个模块,在驱动程序的编写中一般由用户显式的初始化 .owner = THIS_MODULE, 该成员可以防止设备的方法正在被使用时,设备所在模块被卸载。而dev成员和count成员则在cdev_add中才会赋上有效的值。

int cdev_add(struct cdev p, dev_t dev, unsigned count);
该函数向内核注册一个struct cdev结构,即正式通知内核由struct cdev p代表的字符设备已经可以使用了。当然这里还需提供两个参数:(1)第一个设备号 dev,(2)和该设备关联的设备编号的数量。这两个参数直接赋值给struct cdev 的dev成员和count成员。

void cdev_del(struct cdev p);
该函数向内核注销一个struct cdev结构,即正式通知内核由struct cdev p代表的字符设备已经不可以使用了。
从上述的接口讨论中,我们发现对于struct cdev的初始化和注册的过程中,我们需要提供几个东西(1) struct file_operations结构指针;(2)dev设备号;(3)count次设备号个数。

file

file也叫文件描述符表,我们知道每打开一个文件时都会创建一个文件描述符fd,表示一个打开的文件(文件的描述符),由内核在open()时创建,并(将该文件描述符)传递给该文件上进行操作的所有函数,直到最后的close函数,在文件的所有实例都被关闭后,内核会释放这个数据结构。主要供与文件系统对应的设备文件驱动程序使用。

struct file
{
	mode_t f_mode;//表示文件是否可读或可写,FMODE_READ或FMODE_WRITE
	dev_ t  f_rdev ;// 用于/dev/tty
	off_t  f_ops;//当前文件位移
	unsigned short f_flags;//文件标志,O_RDONLY,O_NONBLOCK和O_SYNC
	unsigned short f_count;//打开的文件数目
	unsigned short f_reada;
	struct inode *f_inode;//指向inode的结构指针
	struct file_operations *f_op;//文件索引指针
}

内核驱动和应用交互图
在这里插入图片描述
在这里插入图片描述

/*********************************************************************************
 *      Copyright:  (C) 2017 LingYun IoT Studio <www.iot-yun.com>
 *                  All rights reserved.
 *
 *       Filename:  s3c_led.c
 *    Description:  This file 
 *                 
 *        Version:  1.0.0(07/26/2012~)
 *         Author:  Guo Wenxue <guowenxue@gmail.com>
 *      ChangeLog:  1, Release initial version on "07/26/2012 10:03:40 PM"
 *                 
 ********************************************************************************/

#include <linux/module.h>   /* Every Linux kernel module must include this head */
#include <linux/init.h>     /* Every Linux kernel module must include this head */
#include <linux/kernel.h>   /* printk() */
#include <linux/fs.h>       /* struct fops */
#include <linux/errno.h>    /* error codes */
#include <linux/cdev.h>     /* cdev_alloc()  */
#include <asm/io.h>         /* ioremap()  */
#include <linux/ioport.h>   /* request_mem_region() */

#include <asm/ioctl.h>      /* Linux kernel space head file for macro _IO() to generate ioctl command  */
#include <linux/printk.h>   /* Define log level KERN_DEBUG */

#define DEV_NAME                  "led"
#define LED_NUM                   4

/* Set the LED dev major number */
//#define LED_MAJOR                 79
#ifndef LED_MAJOR
#define LED_MAJOR                 0
#endif

#define DISABLE                   0
#define ENABLE                    1

#define GPIO_INPUT                0x00
#define GPIO_OUTPUT               0x01


#define PLATDRV_MAGIC             0x60
#define LED_OFF                   _IO (PLATDRV_MAGIC, 0x18)
#define LED_ON                    _IO (PLATDRV_MAGIC, 0x19)

#define S3C_GPB_BASE              0x56000010 	
#define S3C_GPB_LEN               0x10        /* 0x56000010~0x56000020  */
#define GPBCON_OFFSET             0
#define GPBDAT_OFFSET             4
#define GPBUP_OFFSET              8

static void __iomem               *gpbbase = NULL;

#define read_reg32(addr)          *(volatile unsigned int *)(addr)
#define write_reg32(addr, val)    *(volatile unsigned int *)(addr) = (val)

int led[LED_NUM] = {5,6,8,10};  /* Four LEDs use GPB5,GPB6,GPB8,GPB10 */
int dev_count = ARRAY_SIZE(led);

int dev_major = LED_MAJOR;
int dev_minor = 0;
int debug = DISABLE;

static struct cdev      *led_cdev;

//硬件初始化函数
static int s3c_hw_init(void)
{
    int            i;
    unsigned int   regval; 

    if(!request_mem_region(S3C_GPB_BASE, S3C_GPB_LEN, "s3c2440 led"))
    {
        printk(KERN_ERR "request_mem_region failure!\n");
        return -EBUSY;
    }

    if( !(gpbbase=(unsigned int  *)ioremap(S3C_GPB_BASE, S3C_GPB_LEN)) )
    {
        release_mem_region(S3C_GPB_BASE, S3C_GPB_LEN);
        printk(KERN_ERR "release_mem_region failure!\n");
        return -ENOMEM;
    }


    for(i=0; i<dev_count; i++)
    {
        /* Set GPBCON register, set correspond GPIO port as input or output mode  */
        regval = read_reg32(gpbbase+GPBCON_OFFSET);
        regval &= ~(0x3<<(2*led[i]));   /* Clear the currespond LED GPIO configure register */
        regval |= GPIO_OUTPUT<<(2*led[i]); /* Set the currespond LED GPIO as output mode */
        write_reg32(gpbbase+GPBCON_OFFSET, regval);

        /* Set GPBUP register, set correspond GPIO port pull up resister as enable or disable  */
        regval = read_reg32(gpbbase+GPBUP_OFFSET);
        regval |= (0x1<<led[i]);  /* Disable pull up resister */
        write_reg32(gpbbase+GPBUP_OFFSET, regval);

        /* Set GPBDAT register, set correspond GPIO port power level as high level or low level */
        regval = read_reg32(gpbbase+GPBDAT_OFFSET);
        regval |= (0x1<<led[i]);  /* This port set to high level, then turn LED off */
        write_reg32(gpbbase+GPBDAT_OFFSET, regval);
    }

    return 0;
}


static void turn_led(int which, unsigned int cmd)
{
    unsigned int   regval; 

    regval = read_reg32(gpbbase+GPBDAT_OFFSET);

    if(LED_ON == cmd)
    {
        regval &= ~(0x1<<led[which]); /*  Turn LED On */
    }
    else if(LED_OFF == cmd)
    {
        regval |= (0x1<<led[which]);  /*  Turn LED off */
    }

    write_reg32(gpbbase+GPBDAT_OFFSET, regval);
}

static void s3c_hw_term(void)
{
    int            i;
    unsigned int   regval; 

    for(i=0; i<dev_count; i++)
    {
        regval = read_reg32(gpbbase+GPBDAT_OFFSET);
        regval |= (0x1<<led[i]);  /* Turn LED off */
        write_reg32(gpbbase+GPBDAT_OFFSET, regval);
    }

    release_mem_region(S3C_GPB_BASE, S3C_GPB_LEN);
    iounmap(gpbbase);
}

//open系统调用具体实现函数
static int led_open(struct inode *inode, struct file *file)
{
    int minor = iminor(inode);

    file->private_data = (void *)minor;

    printk(KERN_DEBUG "/dev/led%d opened.\n", minor);
    return 0;
}
//close系统调用具体实现函数
static int led_release(struct inode *inode, struct file *file)
{
    printk(KERN_DEBUG "/dev/led%d closed.\n", iminor(inode));

    return 0;
}

static void print_help(void)
{
    printk("Follow is the ioctl() commands for %s driver:\n", DEV_NAME);
    printk("Turn LED on command  : %u\n", LED_ON);
    printk("Turn LED off command : %u\n", LED_OFF);

    return;
}
//ioctl系统调用具体实现函数
static long led_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
    int which = (int)file->private_data;//打开的是那个设备就根据file表传参

    switch (cmd)
    {
        case LED_ON:

            turn_led(which, LED_ON);
            break;

        case LED_OFF:
            turn_led(which, LED_OFF);
            break;
    
        default:
            printk(KERN_ERR "%s driver don't support ioctl command=%d\n", DEV_NAME, cmd);
            print_help();
            break;
    }

    return 0;
}

//注册操作方法给设备驱动
static struct file_operations led_fops = 
{
    .owner = THIS_MODULE,
    .open = led_open,
    .release = led_release,
    .unlocked_ioctl = led_ioctl,
};

//模块驱动初始化函数定义
static int __init s3c_led_init(void)
{
    int                    result;
    dev_t                  devno;

    if( 0 != s3c_hw_init() )
    {
        printk(KERN_ERR "s3c2440 LED hardware initialize failure.\n");
        return -ENODEV;
    }

    /*  Alloc the device for driver */
    if (0 != dev_major) /*  Static */
    {
    	//分配设备号
        devno = MKDEV(dev_major, 0);
        result = register_chrdev_region (devno, dev_count, DEV_NAME);
    }
    else
    {
        result = alloc_chrdev_region(&devno, dev_minor, dev_count, DEV_NAME);
        dev_major = MAJOR(devno);
    }

    /*  Alloc for device major failure */
    if (result < 0)
    {
        printk(KERN_ERR "S3C %s driver can't use major %d\n", DEV_NAME, dev_major);
        return -ENODEV;
    } 
    printk(KERN_DEBUG "S3C %s driver use major %d\n", DEV_NAME, dev_major);

	//注册字符设备结构体
    if(NULL == (led_cdev=cdev_alloc()) )
    {
        printk(KERN_ERR "S3C %s driver can't alloc for the cdev.\n", DEV_NAME);
        unregister_chrdev_region(devno, dev_count);
        return -ENOMEM;
    }
    
    led_cdev->owner = THIS_MODULE;
    cdev_init(led_cdev, &led_fops);//结构体初始化

    result = cdev_add(led_cdev, devno, dev_count);//结构体注册到内核
    if (0 != result)
    {   
        printk(KERN_INFO "S3C %s driver can't reigster cdev: result=%d\n", DEV_NAME, result); 
        goto ERROR;
    }

            
    printk(KERN_ERR "S3C %s driver[major=%d] version 1.0.0 installed successfully!\n", DEV_NAME, dev_major);
    return 0;


ERROR:
    printk(KERN_ERR "S3C %s driver installed failure.\n", DEV_NAME);
    cdev_del(led_cdev);
    unregister_chrdev_region(devno, dev_count);
    return result;
}

//模块驱动退出函数定义
static void __exit s3c_led_exit(void)
{
    dev_t devno = MKDEV(dev_major, dev_minor);

    s3c_hw_term();

    cdev_del(led_cdev);
    unregister_chrdev_region(devno, dev_count);

    printk(KERN_ERR "S3C %s driver version 1.0.0 removed!\n", DEV_NAME);

    return ;
}


/* These two functions defined in <linux/init.h> */
module_init(s3c_led_init);
module_exit(s3c_led_exit);

module_param(debug, int, S_IRUGO);
module_param(dev_major, int, S_IRUGO);

MODULE_AUTHOR("guowenxue@LingYun IoT Studio");
MODULE_DESCRIPTION("FL2440 LED linux lowlevel API driver");
MODULE_LICENSE("GPL");


makefile

#*********************************************************************************
#      Copyright:  (C) 2017 LingYun IoT Studio <www.iot-yun.com>
#                  All rights reserved.
#
#       Filename:  Makefile
#    Description:  This Makefile used to compile the driver for FL2440
#                      
#        Version:  1.0.0(10/08/2011~)
#                  Author:  Guo Wenxue <guowenxue@gmail.com>
#      ChangeLog:  1, Release initial version on "11/11/2011 01:29:33 PM"
#                       
#********************************************************************************/

LINUX_SRC = ${shell pwd}/../linux/linux-3.0/
CROSS_COMPILE=/opt/xtools/arm920t/bin/arm-linux-
INST_PATH=/tftp

PWD := $(shell pwd)

EXTRA_CFLAGS+=-DMODULE

obj-m += s3c_led.o

modules:
	@echo ${LINUX_SRC}
	@make -C $(LINUX_SRC) M=$(PWD) modules
	@make clear

uninstall:
	rm -f ${INST_PATH}/*.ko

install: uninstall
	cp -af *.ko ${INST_PATH}

clear:
	@rm -f *.o *.cmd *.mod.c
	@rm -rf  *~ core .depend  .tmp_versions Module.symvers modules.order -f
	@rm -f .*ko.cmd .*.o.cmd .*.o.d

clean: clear
	@rm -f  *.ko


在这里插入图片描述
有了驱动我们还需要测试用例

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/time.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>


#define LED_NUM 4
#define LED_DEVLEN 10

#define PLATDRV_MAGIC 0x60  //魔数用来产生不同的参数而使用的宏
#define LED_OFF _IO(PLATDRV_MAGIC, 0x18) //用_IO宏产生一个随机参数,和驱动内部应该一致
#define LED_ON _IO(PLATDRV_MAGIC, 0x19)

static inline msleep(unsigned long ms)
{
    struct timeval tv;
    tv.tv_sec = ms/1000;
    tv.tv_usec = (ms%1000)*1000;
    select(0, NULL,NULL,NULL, &tv);
}

int main(int argc,char *argv[])
{
    int LED[LED_NUM];
    int cmd;
    char dev_name[LED_DEVLEN];
    int fd[LED_NUM];
    int i;
    if(argv[1] == NULL)
   	{
   		printf("usage:./{filename} --number[0-5]\n");
   		return -1;
    }
	else
		cmd = atoi(argv[1]);

    switch(cmd)
    {
        case 0:
            fd[cmd]=open("/dev/led0",O_RDWR,0755);
            if(fd<0)
                goto err;
            break;
        case 1:
            fd[cmd]=open("/dev/led1",O_RDWR,0755);
            if(fd<0)
                goto err;
            break;
        case 2:
            fd[cmd]=open("/dev/led2",O_RDWR,0755);
            if(fd<0)
                goto err;
            break;
        case 3:
            fd[cmd]=open("/dev/led3",O_RDWR,0755);
            if(fd<0)
                goto err;
            break;
        case 4:
            for(i=0;i<LED_NUM;i++)
            {
                snprintf(dev_name, sizeof(dev_name), "/dev/led%d", i);
                fd[i] = open(dev_name , O_RDWR, 0755);
                if(fd[i]<0)
                    goto err;
            }
    }
    
    if((cmd == 0)||(cmd == 1)||(cmd == 2)||(cmd == 3))
    {
        while(1)
        {
            ioctl(fd[cmd] ,LED_ON);
            msleep(300);
            ioctl(fd[cmd] ,LED_OFF);
            msleep(300);
        }
    }
    else if(cmd==4)
    {
        while(1)
        {
            for(i=0;i<LED_NUM;i++)
            {
                ioctl(fd[i] ,LED_ON);
                msleep(300);
                ioctl(fd[i] ,LED_OFF);
                msleep(300);
            }
        }
    }
    else
    {
        printf("usage:./{filename} --number[0-5]\n");
        return -1;
    }
    
err:
    for(i=0;i<LED_NUM;i++)
    {
        if(fd[i]>0)
            close(fd[i]);
    }
    printf("open device point failure!\n");
    return -1;
}                
                                      

加上makefile

#*********************************************************************************
# Copyright: (C) LingYun IoT Studio <www.iot-yun.com>;
# All rights reserved. #
# Filename: Makefile
# Description: This Makefile used to compile all the C source code file in current
# folder to respective excutable binary files. #
# Version: 1.0.0(10/08/2011~)
# Author: Guo Wenxue <guowenxue@gmail.com>
# ChangeLog: 1, Release initial version on "11/11/2011 01:29:33 PM" #
#********************************************************************************/
PWD=$(shell pwd)
INSTPATH=/tftp
CROSS_COMPILE=/opt/xtools/arm920t/bin/arm-linux- 

export CC=${CROSS_COMPILE}gcc
CFLAGS+=-I `dirname ${PWD}` 

SRCFILES = $(wildcard *.c)
BINARIES=$(SRCFILES:%.c=%)

all: binaries install
	
binaries: ${BINARIES}
	@echo " Compile over" 

%: %.c
	$(CC) -o $@ $< $(CFLAGS)

install:
	cp $(BINARIES) ${INSTPATH}

clean:
	@rm -f version.h
	@rm -f *.o $(BINARIES)
	@rm -rf *.gdb *.a *.so *.elf* distclean: clean
	@rm -f tags cscope*

.PHONY: clean

在这里插入图片描述
再下载到我们的开发板上就可以运行了
测试:
在这里插入图片描述
在这里插入图片描述
LED驱动添加成功。

在这里插入图片描述
创建我们的设备节点。
然后运行就可以在开发板上看到我们的灯轮流在闪。

  • 3
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值