S5PV210 LED驱动程序

用的板子是友善的Smart210由于给的资料太少,网上找到的也很少,这里就分享一下自学的经验给大家。

用的内核是友善给的linux-3.0.8,在Android4.0.3上实验成功。

首先是给的PDF里的用户LED的GPIO很容易迷惑啊,从实际测试来看来4个用户LED分别对应S5PV210_GPJ2(0),S5PV210_GPJ2(1),S5PV210_GPJ2(2),S5PV210_GPJ2(3)。

然后就是没有一个完整的字符设备驱动的编写框架,我这里参考的是李宁的Android深度探究里的驱动框架。

linux都头文件太多里,大家可以从这里学习下linux驱动头文件说明

s5pv210_led.c

#include   
#include  

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


#define DEVICE_NAME "s5pv210_led"
#define DEVICE_COUNT 1
#define SP5V210_LED_MAJOR 0
#define SP5V210_LED_MINOR 234

#define LED_OFF 	 0
#define LED_ON  	 1
#define ALL_LED_OFF  3
#define ALL_LED_ON   4


static int major = SP5V210_LED_MAJOR;
static int minor = SP5V210_LED_MINOR;
static dev_t dev_number;

static struct class *led_class = NULL;

static unsigned long led_table []=
{
	S5PV210_GPJ2(0),
	S5PV210_GPJ2(1),
	S5PV210_GPJ2(2),
	S5PV210_GPJ2(3),
};

static long s5pv210_led_ioctl(struct file *filp, unsigned int cmd,
		unsigned long arg)
{
	int i;
	if (arg > 4)
	{
		return -EINVAL;
	}
	switch(cmd)
	{
		case LED_OFF:
			{
				gpio_set_value(led_table[arg],1);
				printk("LED_OFF\n");
			}	
			break;

		case LED_ON:
			{
				gpio_set_value(led_table[arg],0);
				printk("LED_ON\n");
			}			
			break;
		
		case ALL_LED_OFF:
			for (i = 0; i < 4; i++)		
			{
				gpio_set_value(led_table[i],1);
				printk("ALL_LED_OFF\n");
			}				
			break;

		case ALL_LED_ON:
			for (i = 0; i < 4; i++)
			{
				gpio_set_value(led_table[i],0);
				printk("ALL_LED_ON\n");
			}				
			break;
		
		default:
			{
				printk("ONNE\n");
				return -EINVAL;	
			}
			
	}
}

static struct file_operations dev_fops =
{ .owner = THIS_MODULE, .unlocked_ioctl = s5pv210_led_ioctl};
static struct cdev leds_cdev;

static int led_create_device(void)
{
	int ret = 0;
	int err = 0;

	cdev_init(&leds_cdev, &dev_fops);
	leds_cdev.owner = THIS_MODULE;

	err = alloc_chrdev_region(&leds_cdev.dev, 10, DEVICE_COUNT,DEVICE_NAME);
	if (err < 0)
	{
		printk(KERN_WARNING "alloc_chrdev_region() failed\n");
		return err;
	}
	major = MAJOR(leds_cdev.dev);
	minor = MINOR(leds_cdev.dev);
	dev_number = leds_cdev.dev;

	ret = cdev_add(&leds_cdev,dev_number,DEVICE_COUNT);
	led_class = class_create(THIS_MODULE,DEVICE_NAME);
	device_create(led_class,NULL,dev_number,NULL,DEVICE_NAME);

	return ret;
}

static int s5pv210_led_init(void)
{
	int ret;
	ret = led_create_device();

	gpio_direction_output (S5PV210_GPJ2(0), 1);
	gpio_direction_output (S5PV210_GPJ2(1), 1);
	gpio_direction_output (S5PV210_GPJ2(2), 1);
	gpio_direction_output (S5PV210_GPJ2(3), 1);

	gpio_set_value (S5PV210_GPJ2(0), 0);
	gpio_set_value (S5PV210_GPJ2(1), 0);
	gpio_set_value (S5PV210_GPJ2(2), 0);
	gpio_set_value (S5PV210_GPJ2(3), 0);

	printk(DEVICE_NAME"\tinitialized\n");

	return ret;
}

static void led_destroy_device(void)
{
	device_destroy(led_class,dev_number);
	if (led_class)
		class_destroy(led_class);
	unregister_chrdev_region(dev_number,DEVICE_COUNT);
	return;
}

static void s5pv210_led_exit(void)
{
	led_destroy_device();
	printk(DEVICE_NAME"\texit!\n");
}

module_init(s5pv210_led_init);
module_exit(s5pv210_led_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Fedomn");

Makefile:

ifneq ($(KERNELRELEASE),)
	obj-m:=s5pv210_led.o
else
all:
	make -C /opt/FriendlyARM/mini210/android/linux-3.0.8 M=$(PWD) modules
clean:
	rm -f *.o *.ko *.order *.symvers *.mod.c
endif

测试程序部分:

test_ioctl.c

#include 
#include 
#include 
#include 

int main(int argc, char **argv)
{
	int fd;
	int cmd = 0;
	int arg = 0;

	if (argc < 4)
	{
		printf("Usage: iotlc   \n");
		return 0;
	}
	cmd = atoi(argv[2]);
	arg = atoi(argv[3]);
	printf("dev:%s\n", argv[1]);
	printf("cmd:%d\n", cmd);
	printf("arg:%d\n", arg);

	fd = open(argv[1], 0);
	if (fd < 0)
	{
		perror("open device led error\n");
		exit(1);
	}

	ioctl(fd, cmd ,arg);

	close(fd);

	return 0;	
}

Makefile:

all:test_ioctl.c
	arm-linux-gcc -o test test_ioctl.c -static
clean:
	rm -f led

全部编译成功后,用adb push 把s5pv210_led.ko和test放到/data/local里

通过./test /dev/s5pv210_led 0 0 就可以看到第一个LED灯灭了,表明驱动运行成功。












你可能感兴趣的:(Linux)