喜羊羊系列之【设备-驱动 动态载入进内核】
博客: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