Beaglebone Black LED驱动

首先声明一下工作环境,Beaglebone Black(以下简称BBB)运行:ti-sdk-
am335x-evm-08的linux-3.14.26。虚拟机是ubuntu12.04 假定工作环
境都已经搭建完成。我们需要做的是编写一个LED驱动。并写一个简单的
APP去测试驱动工作状况。
Linux设备分为三类:
1 字符设备
2 块设备
3 网络设备
LED明显归属于第一类,字符设备。接下来先从加载驱动的首先执行的init函数讲解,讲解将主要解释重要函数。完整代码在最后面给出。

/*************************************************
*MAJOR提取主设备号,如果不为零,即手动定义了,那么使用*register_chrdev_region进行注册,第一参数是设备号,在这里只需要注
*册一个设备号,因此第二个参数为1,第三个参数就是起一个名字。如果没有手动
*定义。那么使用alloc_chrdev_region分配。一般推荐使用手动分配,这样
*代码可移植性高。
************************************************/
if(MAJOR(dev->devt))
    ok = register_chrdev_region(dev->devt, 1, dev->name);
} else {
    ok = alloc_chrdev_region(&dev->devt, MINOR(dev->devt), 1, dev->name);
}

/********************************************************
*创建设备类,创建成功后可以在sysfs/class/见到dev->name名字的文件夹
*设备类创建成功后,使用device_create创建设备节点。创建成功后我们就能
*在/dev下找到我们的dev->name名称的LED设备。这样就不需要再使用mknod
*手动创建了。
********************************************************/
dev->dev_class = class_create(THIS_MODULE, dev->name);

dev->device = device_create(dev->dev_class, NULL, dev->devt, dev, dev->name);

/******************************************************
*dev->dev是struct cdev类型,它代表了一个字符设备。首先我们使用
*cdev_init进行初始化,并把dev->fop我们提供的调用函数与字符设备
*关联起来。接下来利用cdev_add加入系统中。在cdev_add中将会将设备加入
*kobject结构里进行管理,并调用kobject_get()增加引用计数。想了解这
*部分内容的可看设备模型。这样系统调用就可以找到我们提供的dev->fop操作
*函数了。
******************************************************/
cdev_init(&dev->dev, &dev->fop);
ok = cdev_add(&dev->dev, dev->devt, 1);

exit()函数则执行与init相反的的工作,注意销毁顺序最好与init的相反。
接下来的要探讨open函数。在open函数里我们进行对LED的GPIO管脚注册初始化。

    //gpio_request()为GPIO注册函数,我理解的硬件资源需要注册是由于
    //比如UART1资源,我们现有驱动A,对UART1进行使用寄存器操作初始化
    //然后再进行收发,从功能上说是实现了的。但是假若此时驱动B也对
    //UART1进行寄存器初始化,然后收发,就会发生冲突。但是内核对这些
    //冲突一无所知。因此我们将资源都统一管理起来,当这个驱动需要时进行
    //注册。当另外一个驱动也需要同一个资源时注册发现资源已被占用而失败
    ret = gpio_request(dev->led_io.io_num, dev-

    //设定管脚方向,0为设定的初始值
    ret = gpio_direction_output(dev->led_io.io_num, 0);

现在LED驱动主要已经讲解完,接下来是写个应用测试一下。应用代码如下。

#include 
#include 
#include 
#include 
#include 
#include 

#define LED_DEV "/dev/led_dev"

int main(int argc, char **argv)
{
    int  fd;
    unsigned int state = 0;

    fd = open(LED_DEV, O_RDWR);
    if(fd < 0){
        printf("led app: can't open file %s\n", LED_DEV);
        exit(1);
    }

    for(;;){
        write(fd, &state, sizeof(unsigned int));
        state = !state;
        sleep(1);
    }

    close(fd);
    exit(0);
}

将驱动,应用编译完成后,可以在BBB端使用tftp命令下载。BBB端tftp是busybox的tftp。用法与ubuntu里的不太一样。也可以在ubuntu端使用scp命令发送过去。insmod驱动后,再执行应用就能看到D31那个LED闪烁。实际过程发现,第一次加载驱动后应用一直提示打不开设备。卸载后重新加载驱动,再次执行应用又可以。
同时发现应用里不能使用fwrite对驱动进行写。测试发现这种情况下驱动端write函数不会得到调用。怀疑由于缓冲没有真正写入。于是改了fwrite()+sync()。也还是不行,希望有人能解答一下疑惑。
下面是完整的LED驱动,写的不好之处请多指教。不胜感激!

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include "led.h"

static int __init led_module_init(void);
static int __exit led_module_exit(void);
static int led_module_open (struct inode *, struct file *);
static int led_module_release (struct inode *, struct file *);
static ssize_t led_module_read (struct file *, char __user *, size_t, loff_t *);
static ssize_t led_module_write (struct file *, const char __user *, size_t, loff_t *);
static long led_module_ioctl (struct file *, unsigned int, unsigned long);

struct led_io_resource_t{
    char name[16]; //端口名称
    uint8_t  state; //记录端口状态信息,已经注册/已释放
    uint32_t io_num; //端口号
    uint8_t   io_dir; //方向0输出1输入
    struct led_io_resource_t *next; //下一个申请的IO资源
};
struct led_module_t{
    const char name[16];    //注册使用的名称

    dev_t devt; //设备号
    struct class *dev_class; //设备类
    struct device *device;  //设备节点
    struct cdev dev;    

    struct file_operations fop; 

    struct led_io_resource_t led_io;
};

static struct led_module_t led_module = {
    .name = "led_dev",

    .devt = MKDEV(LED_DEV_MAJOR, LED_DEV_MINOR),

    .fop = {
        .owner = THIS_MODULE,
        .open  =  led_module_open,
        .release = led_module_release,
        .read     = led_module_read,
        .write    = led_module_write,   
        .unlocked_ioctl = led_module_ioctl,
    },
    .led_io = {
        .name = "",
        .state = GPIO_RELEASE,
        .io_num = LED_GPIO_NUM,
        .io_dir   = OUT_PUT,
        .next     = NULL,
    },

};


static int __init led_module_init(void)
{
    int ok;
    struct led_module_t *dev = &led_module;

    if(MAJOR(dev->devt)){
        //注册手动分配的设备号
        ok = register_chrdev_region(dev->devt, 1, dev->name);
    } else {
        //申请设备号
        ok = alloc_chrdev_region(&dev->devt, MINOR(dev->devt), 1, dev->name);
    }

    if(ok != 0){
        printk(KERN_ERR "CUSE: failed to register chrdev region\n");
        goto register_region_error;
    }

    //创建设备类
    dev->dev_class = class_create(THIS_MODULE, dev->name);
    if(IS_ERR(dev->dev_class)){
        printk(KERN_ERR "CUSE: failed to create chrdev class\n");
        goto class_create_error;
    }
    //创建设备节点
    dev->device = device_create(dev->dev_class, NULL, dev->devt, dev, dev->name);
    if(IS_ERR(dev->device)){
        printk(KERN_ERR "CUSE: failed to create device\n");
        goto device_create_error;
    }

    cdev_init(&dev->dev, &dev->fop);
    //加入散列表中,提供调用管理
    ok = cdev_add(&dev->dev, dev->devt, 1);
    if(ok != 0){
        printk(KERN_ERR "CUSE: failed to add cdev\n");
        goto cdev_add_error;
    }
    return 0;
cdev_add_error:
    device_destroy(dev->dev_class, dev->devt);
device_create_error:
    class_destroy(dev->dev_class);
class_create_error:
    unregister_chrdev_region(dev->devt, 1);
register_region_error:
    return -1;
}
static int __exit led_module_exit(void)
{
    struct led_module_t *dev = &led_module;

    cdev_del(&dev->dev);
    device_destroy(dev->dev_class, dev->devt);
    class_destroy(dev->dev_class);
    unregister_chrdev_region(dev->devt, 1);

    gpio_free(dev->led_io.io_num);
    dev->led_io.state = GPIO_RELEASE;

    return 0;
}
static int led_module_open (struct inode *fi, struct file *fp)
{
    int ret;
    struct led_module_t *dev;

    //fi->i_cdev在cdev_add时传入
    dev = container_of(fi->i_cdev, struct led_module_t, dev);

    //已经打开,直接返回成功
    if(dev->led_io.state == GPIO_REGISTER){
        return 0;
    }

    sprintf(dev->led_io.name, "gpio-%d", dev->led_io.io_num);
    //申请GPIO
    ret = gpio_request(dev->led_io.io_num, dev->led_io.name);
    if(ret < 0){
        printk(KERN_ERR "CUSE: failed to request gpio %d\n", dev->led_io.io_num);
        goto gpio_request_error;
    }

    //设定管脚方向
    ret = gpio_direction_output(dev->led_io.io_num, 0); //0为初始输出值
    if(ret < 0){
        goto set_gpio_direction_failed;
    }

    dev->led_io.state = GPIO_REGISTER;
    return 0;
set_gpio_direction_failed:
    gpio_free(dev->led_io.io_num);
gpio_request_error: 
    return ret;
}
static int led_module_release (struct inode *fi, struct file *fp)
{
    struct led_module_t *dev;

    dev = container_of(fi->i_cdev, struct led_module_t, dev);
    //释放管脚占用
    gpio_free(dev->led_io.io_num);
    dev->led_io.state = GPIO_RELEASE;

    return 0;
}
static ssize_t led_module_read (struct file *fp, char __user *buff, size_t size, loff_t *foff)
{
    uint8_t ret = 0;
    struct led_module_t *dev = &led_module;;


    ret = gpio_get_value(dev->led_io.io_num);

    return copy_to_user(buff, &ret, sizeof(uint8_t));
}
static ssize_t led_module_write (struct file *fp, const char __user *buff, size_t size, loff_t *foff)
{
    uint32_t value = 0;
    struct led_module_t *dev = &led_module;


    copy_from_user(&value, buff, 1);

    gpio_set_value(dev->led_io.io_num, value?1:0);
    printk(KERN_ALERT "%s: write data %d\n", dev->name, value);

    return 0;
}
static long led_module_ioctl (struct file *fp, unsigned int cmd, unsigned long arg)
{
    int ret = 0;
    unsigned int data;
    struct led_module_t *dev = &led_module;

    if(_IOC_TYPE(cmd) != LED_IOCTL_MAGIC){
        printk(KERN_ALERT "%s:  cmd error\n", dev->name);
        return -ENOTTY;
    }

    if(_IOC_NR(cmd) > LED_IOC_MAXNR){
        printk(KERN_ALERT "%s:  cmd error\n", dev->name);
        return -ENOTTY;
    }

    if(_IOC_DIR(cmd) & _IOC_READ){
        switch(cmd){
            case LED_IOCREAD:
                data = gpio_get_value(dev->led_io.io_num);
                if(put_user(data, (unsigned int __user*)arg)){
                    ret = -EFAULT;
                    goto cap_err;
                }
                break;
            default:
                ret = -EINVAL;
        }
    } else if(_IOC_DIR(cmd) & _IOC_WRITE){
        switch(cmd){
            case LED_IOCWRITE:
                if(get_user(data, (unsigned int __user *)arg)){
                    printk(KERN_ALERT "%s:  bad address\n", dev->name);
                    ret = -EFAULT;
                    goto cap_err;
                }

                gpio_set_value(dev->led_io.io_num, data?1:0);
                break;
            default:
                ret = -EINVAL;
        }       
    }else{
        switch(cmd){
            case LED_IOCRESET:
                //nothing to do
                break;
            default:
                ret = -EINVAL;
        }
    }

cap_err:
    return ret;
}
MODULE_LICENSE("GPL");
MODULE_AUTHOR("kwanson");
module_init(led_module_init);
module_exit(led_module_exit);

你可能感兴趣的:(Beaglebone,Black)