沉迷于游戏有一段时间了,该学习了,上次写了helloword驱动,感觉要有点长进,今天就开始搞led驱动。
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/interrupt.h>
#include <linux/ioport.h>
#include <linux/io.h>
#include <mach/hardware.h>
#include <mach/gpio-fns.h>
#include <asm/irq.h>
#include <mach/regs-gpio.h>
#define DEVICE_NAME "leds"
#define LED_MAJOR 231
#define LED_ON 0
#define LED_OFF 1
static struct class *leds_class;
static unsigned long led_pin_table[] = {
S3C2410_GPB(5),
S3C2410_GPB(6),
S3C2410_GPB(7),
S3C2410_GPB(8),
};
static int s3c24xx_leds_open(struct inode *inode, struct file *file)
{
int i;
for(i = 0; i < 4; i++){
s3c2410_gpio_cfgpin(led_pin_table[i], S3C2410_GPIO_OUTPUT);
}
return 0;
}
static int s3c24xx_leds_ioctl(
struct inode *inode,
struct file *file,
unsigned int cmd,
unsigned long arg)
{
if(arg > 4)
return -EINVAL;
switch(cmd){
case LED_ON:
s3c2410_gpio_setpin(led_pin_table[arg], 0);
return 0;
case LED_OFF:
s3c2410_gpio_setpin(led_pin_table[arg], 1);
return 0;
default:
return -EINVAL;
}
}
static struct file_operations s3c24xx_leds_fops = {
.owner = THIS_MODULE,
.open = s3c24xx_leds_open,
.ioctl = s3c24xx_leds_ioctl,
};
static int __init s3c24xx_leds_init(void)
{
int ret;
printk(KERN_INFO "Linux led V1.0\n");
ret = register_chrdev(LED_MAJOR,DEVICE_NAME,&s3c24xx_leds_fops);
if(ret < 0){
printk(KERN_WARNING DEVICE_NAME"canot register major number!\n");
return ret;
}
leds_class = class_create(THIS_MODULE,"leds"); //创建类
if(IS_ERR(leds_class))
{
unregister_chrdev(LED_MAJOR, DEVICE_NAME);
return PTR_ERR(leds_class);
}
device_create(leds_class, NULL, MKDEV(LED_MAJOR,0), NULL, "leds"); //创建设备,在insmod后会自动创建leds设备,不用手动mknod创建节点了
printk(KERN_INFO DEVICE_NAME" initialized!\n");
return 0;
}
static void __exit s3c24xx_leds_exit(void)
{
device_destroy(leds_class, MKDEV(LED_MAJOR, 0));
class_destroy(leds_class);
unregister_chrdev(LED_MAJOR, DEVICE_NAME);
printk(KERN_INFO DEVICE_NAME" exited!\n");
}
module_init(s3c24xx_leds_init);
module_exit(s3c24xx_leds_exit);
MODULE_LICENSE("GPL");
/*
* leds.c - The first kernel app programming
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
void usage(char *exename)
{
printf("Usage:\n");
printf(" %s <led_no> <on/off>\n",exename);
printf(" led_no = 1, 2, 3, 4.\n");
}
int main(int argc, char *argv[])
{
int led_no;
int fd = -1;
if(argc != 3)
goto err;
fd = open("/dev/leds", 0); //打开设备用open,注意open和fopen的区别
if(fd < 0)
{
printf("Can't open /dev/leds\n");
exit(1); //注意exit和return的区别
}
led_no = strtoul(argv[1], 0, 0)-1;
if(led_no > 3 || led_no < 0)
goto err;
if(!strcmp(argv[2], "on")){
ioctl(fd, 0, led_no);
}else if(!strcmp(argv[2], "off")){
ioctl(fd, 1, led_no);
}else{
goto err;
}
close(fd);
return 0;
err:
if(fd > 0)
close(fd);
usage(argv[0]);
return -1;
}
#
# This file is write for the first drivers
# Copyright (c) 2013 sxbg
#obj-m := hello.o
#obj-m := led.o
#hello-objs := hello.o
obj-m += hello.o
obj-m += led.o
ARM_LINUX_KERNEL := /home/sxbg/Downloads/linux
CURRENT_PATH := $(shell pwd)
all:
$(MAKE) -C $(ARM_LINUX_KERNEL) M=$(CURRENT_PATH) modules
clean:
$(MAKE) -C $(ARM_LINUX_KERNEL) M=$(CURRENT_PATH) clean
网上大部分的驱动程序需要手动创建设备节点,此驱动程序的亮点在于自动创建设备节点,程序尽量的规范,请读者特别是和我一样的菜鸟认真体会,其实最好的驱动的代码范例还是内核,笔者的驱动也参见了内核相关代码:
/drivers/isdn/capi/capi.c中有如下代码
static int __init capi_init(void)
{
char *p;
char *compileinfo;
int major_ret;
if ((p = strchr(revision, ':')) != NULL && p[1]) {
strlcpy(rev, p + 2, sizeof(rev));
if ((p = strchr(rev, '$')) != NULL && p > rev)
*(p-1) = 0;
} else
strcpy(rev, "1.0");
major_ret = register_chrdev(capi_major, "capi20", &capi_fops);
if (major_ret < 0) {
printk(KERN_ERR "capi20: unable to get major %d\n", capi_major);
return major_ret;
}
capi_class = class_create(THIS_MODULE, "capi");
if (IS_ERR(capi_class)) {
unregister_chrdev(capi_major, "capi20");
return PTR_ERR(capi_class);
}
device_create(capi_class, NULL, MKDEV(capi_major, 0), NULL, "capi");
#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE
if (capinc_tty_init() < 0) {
device_destroy(capi_class, MKDEV(capi_major, 0));
class_destroy(capi_class);
unregister_chrdev(capi_major, "capi20");
return -ENOMEM;
}
#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */
proc_init();
#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE
#if defined(CONFIG_ISDN_CAPI_CAPIFS) || defined(CONFIG_ISDN_CAPI_CAPIFS_MODULE)
compileinfo = " (middleware+capifs)";
#else
compileinfo = " (no capifs)";
#endif
#else
compileinfo = " (no middleware)";
#endif
printk(KERN_NOTICE "capi20: Rev %s: started up with major %d%s\n",
rev, capi_major, compileinfo);
return 0;
}
static void __exit capi_exit(void)
{
proc_exit();
device_destroy(capi_class, MKDEV(capi_major, 0));
class_destroy(capi_class);
unregister_chrdev(capi_major, "capi20");
#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE
capinc_tty_exit();
#endif
printk(KERN_NOTICE "capi: Rev %s: unloaded\n", rev);
}
where there is a will,there is a way.
只要我们正确的面对,linux驱动没有想象中的那么可怕。
有疑问的细节请参考 嵌入式Linux之我行——LED驱动在2440上的实例开发