用的板子是友善的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灯灭了,表明驱动运行成功。