并发与竞争
并发与竞争简介
因为Linux系统是个多任务操作系统,会存在多个任务同时使用一个资源的问题。就会导致系统奔溃。原因有如下几点:
① 多线程并发访问
② 抢占式并发访问
③ 中断程序并发访问
④ 多核见并发访问
总的来说就是多个程序并发访问同一个资源的问题,解决办法就是阻止多个程序同时访问同一个资源。就是说A程序在访问这个资源时候,B程序访问不了这个资源。需要用到下面4个东东(原子操作、自旋锁、信号量、互斥体)
原子操作
简介
原子操作就是指不能再进一步分割的操作。就是CPU只需要一条指向就可以执行完的操作
原子整形操作API函数
Linux 内核定义了叫做 atomic_t 的结构体来完成整形数据的原子操作。此结构体定义在 include/linux/types.h 文件中,定义如下:
/*32bit*/
typedef struct {
int counter;
} atomic_t;
/*64bit*/
typedef struct {
long long counter;
} atomic64_t;
如果要使用原子操作 API 函数,首先要先定义一个 atomic_t 的变量
atomic_t a;
也可以在定义原子变量的时候给原子变量赋初值
atomic_t b = ATOMIC_INIT(0);
关于原子操作API函数如下
相应的也提供了 64 位原子变量的操作 API 函数; API 函数用法一样,只是将“atomic_”前缀换为“atomic64_”,将 int 换为 long long。
关于原子位操作API函数如下
原子位操作是直接对内存进行操作,不像原子整形变量那样有个 atomic_t 的数据结构。API 函数如下;
原子操作实验
设备树文件修改
在iomuxc节点下的imx6ul-evk节点下创建pinctrl_led节点
pinctrl_led : ledgrp{
fsl,pins<
IMX6UL_PAD_GPIO01_IO03__GPIO1_IO03 0x10B0
>;
};
在根节点/下创建LED节点
gpioled : {
#address-cells = <1>;
#size-cells = <1>;
compatible = "warren-gpioled";
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_led>;
led-gpio = <&gpio1 3 GPIO_ACTIVE_LOW>;
};
驱动程序
#include "linux/types.h"
#include "linux/kernel.h"
#include "linux/delay.h"
#include "linux/ide.h"
#include "linux/init.h"
#include "linux/module.h"
#include "linux/errno.h"
#include "linux/gpio.h"
#include "linux/cdev.h"
#include "linux/device.h"
#include "linux/of.h"
#include "linux/of_address.h"
#include "linux/of_gpio.h"
#include "asm/mach/map.h"
#include "asm/uaccess.h"
#include "asm/io.h"
#define GPIOLED_CNT 1
#define GPIOLED_NAME "led"
#define LEDON 1
#define LEDOFF 0
struct gpioled_dev{
dev_t devid;
struct cdev cdev;
struct class *class;
struct device *device;
int major;
int minor;
struct device_node *nd;
int led_gpio;
atomic_t lock;
};
struct gpioled_dev gpioled;
static int led_open(struct inode *inode, struct file *filp)
{
if(!atomic_dec_and_test(&gpioled.lock)){
atomic_inc(&gpioled.lock);
return -EBUSY;
}
filp->private_data = &gpioled;
return 0;
}
static int led_release(struct inode *inode, struct file *filp)
{
struct gpioled_dev *dev = filp->private_data;
atomic_inc(&dev->lock);
return 0;
}
static ssize_t led_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
{
return 0;
}
static ssize_t led_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{
int retvalue;
unsigned char databuf[1];
unsigned char ledstat;
struct gpioled_dev *dev = filp->private_data;
retvalue = copy_from_user(databuf, buf, cnt);
if(retvalue < 0){
printk("kernel write failed!\r\n");
return -EFAULT;
}
ledstat = databuf[0];
if(ledstat == LEDON){
gpio_set_value(dev->led_gpio, 0);
}else if(ledstat == LEDOFF){
gpio_set_value(dev->led_gpio, 1);
}
return 0;
}
static struct file_operations gpioled_fops = {
.owner = THIS_MODULE,
.open = led_open,
.release = led_release,
.write = led_write,
.read = led_read,
};
static int __init led_init(void)
{
int ret = 0;
atomic_set(&gpioled.lock, 1);
gpioled.nd = of_find_node_by_path("/led");
if(gpioled.nd == NULL){
printk("gpioled node find failed!\r\n");
return -EINVAL;
}else{
printk("gpioled node find success!\r\n");
}
gpioled.led_gpio = of_get_named_gpio(gpioled.nd, "led-gpio", 0);
if(gpioled.led_gpio < 0){
printk("can't get led-gpio\r\n");
return -EINVAL;
}
printk("led-gpio num = %d\r\n", gpioled.led_gpio);
ret = gpio_direction_output(gpioled.led_gpio, 1);
if(ret < 0){
printk("can't set gpio!\r\n");
}
if(gpioled.major){
gpioled.devid = MKDEV(gpioled.major, 0);
register_chrdev_region(gpioled.devid, GPIOLED_CNT, GPIOLED_NAME);
}else{
alloc_chrdev_region(&gpioled.devid, 0, GPIOLED_CNT, GPIOLED_NAME);
gpioled.major = MAJOR(gpioled.devid);
gpioled.minor = MINOR(gpioled.devid);
}
printk("gpioled major = %d, minor = %d\r\n", gpioled.major, gpioled.minor);
gpioled.cdev.owner = THIS_MODULE;
cdev_init(&gpioled.cdev, &gpioled_fops);
cdev_add(&gpioled.cdev, gpioled.devid, GPIOLED_CNT);
gpioled.class = class_create(THIS_MODULE, GPIOLED_NAME);
if(IS_ERR(gpioled.class)){
return PTR_ERR(gpioled.class);
}
gpioled.device = device_create(gpioled.class, NULL, gpioled.devid, NULL, GPIOLED_NAME);
if(IS_ERR(gpioled.device)){
return PTR_ERR(gpioled.device);
}
return 0;
}
static void __exit led_exit(void)
{
cdev_del(&gpioled.cdev);
unregister_chrdev_region(gpioled.devid, GPIOLED_CNT);
device_destroy(gpioled.class, gpioled.devid);
class_destroy(gpioled.class);
}
module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("warren");
应用程序
#include "stdio.h"
#include "unistd.h"
#include "sys/types.h"
#include "sys/stat.h"
#include "fcntl.h"
#include "stdlib.h"
#include "string.h"
int main(int argc, int *argv[])
{
int fd;
char *filename = NULL;
char data = 0;
int retvalue = 0;
int cnt = 0;
if(argc != 3){
printf("error usage!\r\n");
return -1;
}
filename = (char*)argv[1];
fd = open(filename, O_RDWR);
if(fd < 0){
printf("file %s open failed\r\n", filename);
return -1;
}
data = atoi(argv[2]);
retvalue = write(fd, &data, 1);
if(retvalue < 0){
printf("write %s failed\r\n", filename);
close(fd);
return -1;
}
while(1){
sleep (1);
cnt++;
printf("App runing times : %d\r\n", cnt);
if(cnt > 5){
break;
}
}
printf("App runing finished\r\n");
retvalue = close(fd);
if(retvalue < 0){
printf("file %s close failed\r\n", filename);
return -1;
}
return 0;
}
自旋锁
简介
原子操作只能保护一个变量的值,并不能保护结构体变量。或者说原子操作的进一步使用就是自旋锁。
自旋锁的缺点:那就等待自旋锁的线程会一直处于自旋状态,这样会浪费处理器时间,降低系统性能,所以自旋锁的持有时间不能太长。
Linux 内核使用结构体 spinlock_t 表示自旋锁,结构体定义如下所示:
typedef struct spinlock {
union {
struct raw_spinlock rlock;
#ifdef CONFIG_DEBUG_LOCK_ALLOC
# define LOCK_PADSIZE (offsetof(struct raw_spinlock, dep_map))
struct {
u8 __padding[LOCK_PADSIZE];
struct lockdep_map dep_map;
};
#endif
};
} spinlock_t;
在使用自旋锁之前,肯定要先定义一个自旋锁变量,定义方法如下
spinlock_t lock;
自旋锁API函数
被自旋锁保护的临界区一定不能调用任何能够引起睡眠和阻塞的API 函数,否则的话会可能会导致死锁现象的发生。
比如有两个线程AB。A线程加锁后进入睡眠了。B线程因为获取不到锁,导致B线程一直死等。就会导致锁死现象。
还有种情况就是在中断里面加锁,假设在中断A中加锁,还没来得及解锁就被新中断打断或者被抢占。导致锁死。所以说在中断中使用自旋锁时候需要在获取锁之前先禁止本地中断。在释放锁之后使能本地中断。Linux内核提供了相应的API函数,如下。
不推荐使用spin_lock_irq/spin_unlock_irq。建议使用 spin_lock_irqsave/ spin_unlock_irqrestore,因为这一组函数会保存中断状态,在释放锁的时候会恢复中断状态。
一般在线程中使用 spin_lock_irqsave/spin_unlock_irqrestore,在中断中使用 spin_lock/spin_unlock。
在Linux中,中断处理被分为两个主要部分:顶半部(上半部,Top Half)和下半部(Bottom Half)。这种设计是为了解决中断处理程序执行时间过长和中断丢失的问题,同时确保关键任务能够迅速得到响应。
顶半部的主要任务是快速处理中断,并暂时关闭中断请求。它主要处理与硬件紧密相关或时间敏感的事情,比如对硬件的读取寄存器中的中断状态并清除中断标志。当一个中断发生时,顶半部会进行相应的硬件操作,并将中断例程的下半部挂到该设备的下半部执行队列中。由于顶半部的执行速度很快,它可以服务更多的中断请求
下半部用于延迟处理顶半部未完成的工作。与顶半部不同,下半部是可以被中断的,这意味着它可以在执行过程中被新的中断打断。下半部几乎执行了中断处理程序的所有任务,但它并不是非常紧急的,通常比较耗时。因此,它的执行时机由系统自行安排,并不在中断服务上下文中执行。
下半部(BH)也会竞争共享资源,如果要在下半部里面使用自旋锁,可以使用下列API函数
自旋锁实验
设备树文件修改
驱动程序编写
#include "linux/types.h"
#include "linux/kernel.h"
#include "linux/delay.h"
#include "linux/ide.h"
#include "linux/init.h"
#include "linux/module.h"
#include "linux/errno.h"
#include "linux/gpio.h"
#include "linux/cdev.h"
#include "linux/device.h"
#include "linux/of.h"
#include "linux/of_address.h"
#include "linux/of_gpio.h"
#include "asm/mach/map.h"
#include "asm/uaccess.h"
#include "asm/io.h"
#define GPIOLED_CNT 1
#define GPIOLED_NAME "led"
#define LEDON 1
#define LEDOFF 0
struct gpioled_dev{
dev_t devid;
struct cdev cdev;
struct class *class;
struct device *device;
int major;
int minor;
struct device_node *nd;
int led_gpio;
int dev_stats;
spinlock_t lock;
};
struct gpioled_dev gpioled;
static int led_open(struct inode *inode, struct file *filp)
{
unsigned long flags;
filp->private_data = &gpioled;
spin_lock_irqsave(&gpioled.lock, flags); //加锁
if(gpioled.dev_stats){ //设备正在使用
spin_unlock_irqrestore(&gpioled.lock, flags); //解锁
return -EBUSY;
}
gpioled.dev_stats++;
spin_unlock_irqrestore(&gpioled.lock, flags); //解锁
return 0;
}
static int led_release(struct inode *inode, struct file *filp)
{
struct gpioled_dev *dev = filp->private_data;
unsigned long flags;
spin_lock_irqsave(&dev->lock, flags); //加锁
if(gpioled.dev_stats){
gpioled.dev_stats--;
}
spin_unlock_irqrestore(&dev->lock, flags); //解锁
return 0;
}
static ssize_t led_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
{
return 0;
}
static ssize_t led_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{
int retvalue;
unsigned char databuf[1];
unsigned char ledstat;
struct gpioled_dev *dev = filp->private_data;
retvalue = copy_from_user(databuf, buf, cnt);
if(retvalue < 0){
printk("kernel write failed!\r\n");
return -EFAULT;
}
ledstat = databuf[0];
if(ledstat == LEDON){
gpio_set_value(dev->led_gpio, 0);
}else if(ledstat == LEDOFF){
gpio_set_value(dev->led_gpio, 1);
}
return 0;
}
static struct file_operations gpioled_fops = {
.owner = THIS_MODULE,
.open = led_open,
.release = led_release,
.write = led_write,
.read = led_read,
};
static int __init led_init(void)
{
int ret = 0;
spin_lock_init(&gpioled.lock);
gpioled.nd = of_find_node_by_path("/led");
if(gpioled.nd == NULL){
printk("gpioled node find failed!\r\n");
return -EINVAL;
}else{
printk("gpioled node find success!\r\n");
}
gpioled.led_gpio = of_get_named_gpio(gpioled.nd, "led-gpio", 0);
if(gpioled.led_gpio < 0){
printk("can't get led-gpio\r\n");
return -EINVAL;
}
printk("led-gpio num = %d\r\n", gpioled.led_gpio);
ret = gpio_direction_output(gpioled.led_gpio, 1);
if(ret < 0){
printk("can't set gpio!\r\n");
}
if(gpioled.major){
gpioled.devid = MKDEV(gpioled.major, 0);
register_chrdev_region(gpioled.devid, GPIOLED_CNT, GPIOLED_NAME);
}else{
alloc_chrdev_region(&gpioled.devid, 0, GPIOLED_CNT, GPIOLED_NAME);
gpioled.major = MAJOR(gpioled.devid);
gpioled.minor = MINOR(gpioled.devid);
}
printk("gpioled major = %d, minor = %d\r\n", gpioled.major, gpioled.minor);
gpioled.cdev.owner = THIS_MODULE;
cdev_init(&gpioled.cdev, &gpioled_fops);
cdev_add(&gpioled.cdev, gpioled.devid, GPIOLED_CNT);
gpioled.class = class_create(THIS_MODULE, GPIOLED_NAME);
if(IS_ERR(gpioled.class)){
return PTR_ERR(gpioled.class);
}
gpioled.device = device_create(gpioled.class, NULL, gpioled.devid, NULL, GPIOLED_NAME);
if(IS_ERR(gpioled.device)){
return PTR_ERR(gpioled.device);
}
return 0;
}
static void __exit led_exit(void)
{
cdev_del(&gpioled.cdev);
unregister_chrdev_region(gpioled.devid, GPIOLED_CNT);
device_destroy(gpioled.class, gpioled.devid);
class_destroy(gpioled.class);
}
module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("warren");
实验程序编写
实验现象
信号量
信号量简介
信号量就是自旋锁的升级版本,自旋锁在解锁失败时候会死等,直到解锁成功为止。信号量则在获取不到时候进入到休眠状态。可以看到信号量的开销要比自旋锁大,因为信号量使线程进入休眠状态以后会切换线程,切换线程就会有开销。
信号量的特点:.
- 因为信号量可以使等待资源线程进入休眠状态,因此适用于那些占用资源比较久的场
合
- 因此信号量不能用于中断中,因为信号量会引起休眠,中断不能休眠
- 如果共享资源的持有时间比较短,那就不适合使用信号量了,因为频繁的休眠、切换线程引起的开销要远大于信号量带来的那点优势
信号量就是生产者与消费者的关系,因为它允许多个线程同时访问共享资源。
Linux 内核使用 semaphore 结构体表示信号量,结构体内容如下所示
struct semaphore {
raw_spinlock_t lock;
unsigned int count;
struct list_head wait_list;
};
信号量API函数
信号量的使用如下:
struct semaphore sem; /*定义信号量*/
sema_init(&sem, 1); /*初始化信号量*/
down(&sem); /*申请信号量, 申请不到会进入到睡眠但睡眠可以被信号打断*/
down_trylock(&sem);/*申请信号量, 申请不到立即返回非0,不会进入到睡眠*/
dowm_interruptible(&sem);/*申请信号量, 申请不到会进入到睡眠, 但睡眠可以被信号打断*/
up(&sem); /*释放信号量*/
驱动程序编写
#include "linux/types.h"
#include "linux/kernel.h"
#include "linux/delay.h"
#include "linux/ide.h"
#include "linux/init.h"
#include "linux/module.h"
#include "linux/errno.h"
#include "linux/gpio.h"
#include "linux/cdev.h"
#include "linux/device.h"
#include "linux/of.h"
#include "linux/of_address.h"
#include "linux/of_gpio.h"
#include "asm/mach/map.h"
#include "asm/uaccess.h"
#include "asm/io.h"
#include "linux/semaphore.h"
#define GPIOLED_CNT 1
#define GPIOLED_NAME "led"
#define LEDON 1
#define LEDOFF 0
struct gpioled_dev{
dev_t devid;
struct cdev cdev;
struct class *class;
struct device *device;
int major;
int minor;
struct device_node *nd;
int led_gpio;
struct semaphore sem;
};
struct gpioled_dev gpioled;
static int led_open(struct inode *inode, struct file *filp)
{
filp->private_data = &gpioled;
if(down_interruptible(&gpioled.sem)){
return -ERESTARTSYS;
}
return 0;
}
static int led_release(struct inode *inode, struct file *filp)
{
struct gpioled_dev *dev = filp->private_data;
up(&dev->sem);
return 0;
}
static ssize_t led_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
{
return 0;
}
static ssize_t led_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{
int retvalue;
unsigned char databuf[1];
unsigned char ledstat;
struct gpioled_dev *dev = filp->private_data;
retvalue = copy_from_user(databuf, buf, cnt);
if(retvalue < 0){
printk("kernel write failed!\r\n");
return -EFAULT;
}
ledstat = databuf[0];
if(ledstat == LEDON){
gpio_set_value(dev->led_gpio, 0);
}else if(ledstat == LEDOFF){
gpio_set_value(dev->led_gpio, 1);
}
return 0;
}
static struct file_operations gpioled_fops = {
.owner = THIS_MODULE,
.open = led_open,
.release = led_release,
.write = led_write,
.read = led_read,
};
static int __init led_init(void)
{
int ret = 0;
sema_init(&gpioled.sem, 1);
gpioled.nd = of_find_node_by_path("/led");
if(gpioled.nd == NULL){
printk("gpioled node find failed!\r\n");
return -EINVAL;
}else{
printk("gpioled node find success!\r\n");
}
gpioled.led_gpio = of_get_named_gpio(gpioled.nd, "led-gpio", 0);
if(gpioled.led_gpio < 0){
printk("can't get led-gpio\r\n");
return -EINVAL;
}
printk("led-gpio num = %d\r\n", gpioled.led_gpio);
ret = gpio_direction_output(gpioled.led_gpio, 1);
if(ret < 0){
printk("can't set gpio!\r\n");
}
if(gpioled.major){
gpioled.devid = MKDEV(gpioled.major, 0);
register_chrdev_region(gpioled.devid, GPIOLED_CNT, GPIOLED_NAME);
}else{
alloc_chrdev_region(&gpioled.devid, 0, GPIOLED_CNT, GPIOLED_NAME);
gpioled.major = MAJOR(gpioled.devid);
gpioled.minor = MINOR(gpioled.devid);
}
printk("gpioled major = %d, minor = %d\r\n", gpioled.major, gpioled.minor);
gpioled.cdev.owner = THIS_MODULE;
cdev_init(&gpioled.cdev, &gpioled_fops);
cdev_add(&gpioled.cdev, gpioled.devid, GPIOLED_CNT);
gpioled.class = class_create(THIS_MODULE, GPIOLED_NAME);
if(IS_ERR(gpioled.class)){
return PTR_ERR(gpioled.class);
}
gpioled.device = device_create(gpioled.class, NULL, gpioled.devid, NULL, GPIOLED_NAME);
if(IS_ERR(gpioled.device)){
return PTR_ERR(gpioled.device);
}
return 0;
}
static void __exit led_exit(void)
{
cdev_del(&gpioled.cdev);
unregister_chrdev_region(gpioled.devid, GPIOLED_CNT);
device_destroy(gpioled.class, gpioled.devid);
class_destroy(gpioled.class);
}
module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("warren");
应用程序编写
实验现象
互斥体
互斥体简介
互斥体就是信号量的特殊化,一次只允许一个线程拥有资源的所有权。将信号量的值设置为 1 就可以使用信号量进行互斥访问了。Linux 内核使用 mutex 结构体表示互斥体,定义如下。
struct mutex {
/* 1: unlocked, 0: locked, negative: locked, possible waiters */
atomic_t count;
spinlock_t wait_lock;
};
mutex 之前要先定义一个 mutex 变量
struct mutex lock;
使用 mutex 的时候要注意如下几点
{mutex 可以导致休眠,因此不能在中断中使用 mutex,中断中只能使用自旋锁}
{和信号量一样, mutex 保护的临界区可以调用引起阻塞的 API 函数}
{因为一次只有一个线程可以持有 mutex,因此,必须由 mutex 的持有者释放 mutex。并
且 mutex 不能递归上锁和解锁}
互斥体API函数
互斥体的使用如下
struct mutex lock; /*互斥体定义*/
mutex_init(&lock); /*互斥体初始化*/
mutex_lock(&lock); /*获取互斥锁, 获取不到就睡觉且不能被打断*/
mutex_trylock(&lock);/*获取互斥锁,获取不到直接返回0*/
mutex_is_locked(&lock);/*查看互斥锁是否被获取,是返回1 否返回0*/
mutex_lock_interruptible(&lock);/*获取互斥锁,获取不到就睡觉,且可以被信号打断*/
mutex_unlock(&lock);
驱动程序编写
#include "linux/types.h"
#include "linux/kernel.h"
#include "linux/delay.h"
#include "linux/ide.h"
#include "linux/init.h"
#include "linux/module.h"
#include "linux/errno.h"
#include "linux/gpio.h"
#include "linux/cdev.h"
#include "linux/device.h"
#include "linux/of.h"
#include "linux/of_address.h"
#include "linux/of_gpio.h"
#include "asm/mach/map.h"
#include "asm/uaccess.h"
#include "asm/io.h"
#include "linux/mutex.h"
#define GPIOLED_CNT 1
#define GPIOLED_NAME "led"
#define LEDON 1
#define LEDOFF 0
struct gpioled_dev{
dev_t devid;
struct cdev cdev;
struct class *class;
struct device *device;
int major;
int minor;
struct device_node *nd;
int led_gpio;
struct mutex lock;
};
struct gpioled_dev gpioled;
static int led_open(struct inode *inode, struct file *filp)
{
filp->private_data = &gpioled;
if(mutex_lock_interruptible(&gpioled.lock)){
return -ERESTARTSYS;
}
return 0;
}
static int led_release(struct inode *inode, struct file *filp)
{
struct gpioled_dev *dev = filp->private_data;
mutex_unlock(&dev->lock);
return 0;
}
static ssize_t led_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
{
return 0;
}
static ssize_t led_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{
int retvalue;
unsigned char databuf[1];
unsigned char ledstat;
struct gpioled_dev *dev = filp->private_data;
retvalue = copy_from_user(databuf, buf, cnt);
if(retvalue < 0){
printk("kernel write failed!\r\n");
return -EFAULT;
}
ledstat = databuf[0];
if(ledstat == LEDON){
gpio_set_value(dev->led_gpio, 0);
}else if(ledstat == LEDOFF){
gpio_set_value(dev->led_gpio, 1);
}
return 0;
}
static struct file_operations gpioled_fops = {
.owner = THIS_MODULE,
.open = led_open,
.release = led_release,
.write = led_write,
.read = led_read,
};
static int __init led_init(void)
{
int ret = 0;
mutex_init(&gpioled.lock);
gpioled.nd = of_find_node_by_path("/led");
if(gpioled.nd == NULL){
printk("gpioled node find failed!\r\n");
return -EINVAL;
}else{
printk("gpioled node find success!\r\n");
}
gpioled.led_gpio = of_get_named_gpio(gpioled.nd, "led-gpio", 0);
if(gpioled.led_gpio < 0){
printk("can't get led-gpio\r\n");
return -EINVAL;
}
printk("led-gpio num = %d\r\n", gpioled.led_gpio);
ret = gpio_direction_output(gpioled.led_gpio, 1);
if(ret < 0){
printk("can't set gpio!\r\n");
}
if(gpioled.major){
gpioled.devid = MKDEV(gpioled.major, 0);
register_chrdev_region(gpioled.devid, GPIOLED_CNT, GPIOLED_NAME);
}else{
alloc_chrdev_region(&gpioled.devid, 0, GPIOLED_CNT, GPIOLED_NAME);
gpioled.major = MAJOR(gpioled.devid);
gpioled.minor = MINOR(gpioled.devid);
}
printk("gpioled major = %d, minor = %d\r\n", gpioled.major, gpioled.minor);
gpioled.cdev.owner = THIS_MODULE;
cdev_init(&gpioled.cdev, &gpioled_fops);
cdev_add(&gpioled.cdev, gpioled.devid, GPIOLED_CNT);
gpioled.class = class_create(THIS_MODULE, GPIOLED_NAME);
if(IS_ERR(gpioled.class)){
return PTR_ERR(gpioled.class);
}
gpioled.device = device_create(gpioled.class, NULL, gpioled.devid, NULL, GPIOLED_NAME);
if(IS_ERR(gpioled.device)){
return PTR_ERR(gpioled.device);
}
return 0;
}
static void __exit led_exit(void)
{
cdev_del(&gpioled.cdev);
unregister_chrdev_region(gpioled.devid, GPIOLED_CNT);
device_destroy(gpioled.class, gpioled.devid);
class_destroy(gpioled.class);
}
module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("warren");
应用程序编写
实验现象
完结
评论 (0)