博客:http://blog.csdn.net/muyang_ren
这篇和设备驱动编入内核做对照
led.h
#ifndef _HEAD_H
#define _HEAD_H
#define MAGIC 'h'
#define LED_ON _IOW(MAGIC,1,int)
#define LED_OFF _IOW(MAGIC,0,int)
struct led_device{
dev_t devno;
unsigned int led_major;
struct cdev *led_cdev;
struct class *led_class;
struct device *led_device;
};
#endif
平台设备
dev_led.c
#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include "led.h"
static void led_release(struct device *dev){
}
static struct platform_device s5pv210_led_device_lhy = {
.name = "s5pv210_led_lhy",
.id = 1,
.dev = {
.release = led_release, //开发模式下,这个真不能少!
},
};
static int __init dev_led_init(void){
platform_device_register(&s5pv210_led_device_lhy);
return 0;
}
static void __exit dev_led_exit(void){
platform_device_unregister(&s5pv210_led_device_lhy);
}
module_init(dev_led_init);
module_exit(dev_led_exit);
MODULE_LICENSE("GPL");
平台驱动
led_drv.c
#include<linux/init.h>
#include<linux/module.h>
#include<linux/fs.h> //register_chrled
#include<linux/device.h> //class_create/ledice_create
#include<linux/slab.h> //kmalloc
#include<asm/uaccess.h> //copy_to_user/copy_from_user
#include<asm/io.h> //ioremap
#include<linux/gpio.h> //gpio_request
#include <plat/gpio-cfg.h> //s3c_gpio_cfgpin
#include <linux/cdev.h> //cdev_alloc
#include <linux/platform_device.h>
#include"led.h"
static struct led_device *led_drv;
static int led_open(struct inode *inode, struct file *file)
{
printk(KERN_INFO"%s()-%d\n", __func__, __LINE__);
s3c_gpio_cfgpin(S5PV210_GPC0(3),S3C_GPIO_OUTPUT);
s3c_gpio_cfgpin(S5PV210_GPC0(4),S3C_GPIO_OUTPUT);
return 0;
}
static ssize_t led_read(struct file *file, char __user *buf, size_t count, loff_t *offset)
{
printk(KERN_INFO"%s()-%d\n", __func__, __LINE__);
return count;
}
ssize_t led_write(struct file *file, const char __user *buf, size_t count, loff_t *offset)
{
printk(KERN_INFO"%s()-%d\n", __func__, __LINE__);
return 0;
}
static long led_ioctl(struct file *file, unsigned int cmd, unsigned long val)
{
printk(KERN_INFO"%s()-%d\n", __func__, __LINE__);
printk(KERN_INFO"cmd=%d arg=%ld\n", cmd, val);
switch(cmd)
{
case LED_ON:
gpio_set_value(S5PV210_GPC0(val),1);
break;
case LED_OFF:
gpio_set_value(S5PV210_GPC0(val),0);
break;
default:
break;
}
return 0;
}
//硬件操作方法
static struct file_operations led_fops={
.owner = THIS_MODULE,
.open = led_open,
.write = led_write,
.read = led_read,
.unlocked_ioctl = led_ioctl,
};
static int s5pv210_led_probe(struct platform_device *pdrv){
int ret;
led_drv = kmalloc(sizeof(struct led_device),GFP_KERNEL);
if(led_drv==NULL){
printk(KERN_ERR"no memory malloc for fs210_led\n");
return -ENOMEM;
}
/*1. 动态注冊/申请主设备*/
ret=alloc_chrdev_region(&led_drv->devno,0,1,"dev_module");
if (ret < 0) {
printk(KERN_ERR "unable to get major\n");
return -EFAULT;
goto out_err_1;
}
//从设备号中分离出主设备号
led_drv->led_major = MAJOR(led_drv->devno);
/*为cdev分配空间*/
led_drv->led_cdev = cdev_alloc();
/*注冊硬件操作方法/初始化cdev*/
cdev_init(led_drv->led_cdev,&led_fops);
/*注冊字符设备*/
cdev_add(led_drv->led_cdev,led_drv->devno,1);
/*2. 创建设备类*/
led_drv->led_class=class_create(THIS_MODULE,"led_class");
if (IS_ERR(led_drv->led_class)) {
printk(KERN_ERR "class_create() failed for led_class\n");
ret = -ENODATA;
goto out_err_2;
}
/*3. 创建设备文件*/
led_drv->led_device=device_create(led_drv->led_class,NULL,MKDEV(led_drv->led_major,0),NULL,"led"); // /led/xxx
if (IS_ERR(led_drv->led_device)) {
printk(KERN_ERR "device_create failed for led_device\n");
ret = -ENODEV;
goto out_err_3;
}
/*申请GPC0_3,4引脚资源*/
gpio_request(S5PV210_GPC0(3),"LED1");
gpio_request(S5PV210_GPC0(4),"LED2");
return 0;
out_err_3:
class_destroy(led_drv->led_class);
out_err_2:
unregister_chrdev(led_drv->led_major,"led_module");
out_err_1:
kfree(led_drv);
return ret;
}
static int s5pv210_led_remove(struct platform_device *pdrv){
unregister_chrdev(led_drv->led_major,"led_module");
device_destroy(led_drv->led_class,MKDEV(led_drv->led_major,0));
class_destroy(led_drv->led_class);
gpio_free(S5PV210_GPC0(3));
gpio_free(S5PV210_GPC0(4));
kfree(led_drv);
return 0;
}
struct platform_device_id led_ids[]={
[0]={
.name = "s5pv210_led_lhy",
.driver_data = 0,
},
};
static struct platform_driver s5pv210_led_driver = {
.probe = s5pv210_led_probe,
.remove = s5pv210_led_remove,
.driver = { .name = "s5pv210_led_lhy",
.owner = THIS_MODULE, },
.id_table = led_ids,
};
static int __init led_init(void)
{
platform_driver_register(&s5pv210_led_driver);
return 0;
}
static void __exit led_exit(void)
{
platform_driver_unregister(&s5pv210_led_driver);
}
module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("GPL");
測试程序:
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <sys/ioctl.h>
#include "led.h"
/*
** ./led_test on num 点亮LED1/2
** ./led_test off num 熄灭LED1/2
*/
int main(int argc, char *argv[])
{
if (argc != 3)
printf("input:< %s on led_num >or< %s off led_num>\n",argv[0],argv[0]);
int fd;
unsigned int cmd=0;
unsigned long val=0;
fd=open("/dev/led", O_RDWR);
if(fd<0){
perror("open failed!\n");
exit(1);
}
if(strcmp(argv[1],"on")==0){
cmd=LED_ON;
}else if(strcmp(argv[1],"off")==0){
cmd=LED_OFF;
}else{
printf("err input!\n");
}
val = atoi(argv[2]);
if(ioctl(fd,cmd,val)<0){
perror("ioctl failed!\n");
exit(1);
}
close(fd);
return 0;
}
Makefile
ifeq ($(KERNELRELEASE),)
KERNELDIR =/home/farsight/work/kernel/linux-3.0.8
PWD =$(shell pwd)
modules:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
arm-none-linux-gnueabi-gcc led_test.c -o led_test
cp led_drv.ko dev_led.ko led_test /opt/filesystem/s5pv210
modules_install:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules_install
clean:
rm -rf *.so *.o *.ko *test .tmp_versions *.mod.c *.order *.symvers
else
obj-m :=led_drv.o dev_led.o
endif