下面是我以前写的AT91开发板的了led的驱动,希望对初学者有帮助
1:直接操作地址的编写方式
#include <linux/mm.h>
#include <asm/io.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/rtc.h> /* get the user-level API */
#include <linux/list.h>
#include <linux/timer.h>
#include <linux/delay.h>
#include <mach/at91_pio.h>
#include <mach/gpio.h>
#include <linux/kdev_t.h>
#include <linux/types.h>
#include <linux/cdev.h>
#include <linux/platform_device.h>
#include <linux/version.h>
/*通过测试也是可以的*/
#define DEV_NAME "led" /* 加载模式后,执行”cat /proc/devices”命令看到的设备名称 */
#define DEV_MAJOR 231 /* 主设备号 */
//#define DEV_MAJOR 0
#define DEV_MINOR 0
struct led_dev
{
dev_t devno; //变量是设备号
int major;
int minor;
struct class_device *class_dev;
struct class *class;
struct cdev cdev;
};
#define LED_ON 0 /* 应用程序执行ioctl(fd, cmd, arg)时的第2个参数 */
#define LED_OFF 1
static void __iomem *pio_base;
/* 应用程序对设备文件/dev/led执行open(...)时, * 就会调用led_open函数 */
static int baite_led_open( struct inode *inode,
struct file *file)
{
pio_base = ioremap(0xFFFFF400,512); //PIOA开始的地址 以及空间512字节
writel(0x00000040, pio_base + PIO_PER); //PIO的使能寄存器 表示向该寄存器写入0x00000040的数据
writel(0x00000040, pio_base + PIO_PUER); //pull—up 使能寄存器 表示向该寄存器写入0x00000040的数据
writel(0x00000040, pio_base + PIO_ODSR); //输出数据状态寄存器 表示向该寄存器写入0x00000040的数据
writel(0x00000040, pio_base + PIO_OER); //输出使能寄存器 表示向该寄存器写入0x00000040的数据
printk(KERN_INFO " open LED\n");
return 0;
}
static int baite_led_ioctl( struct inode *inode,
struct file *filp,
unsigned int cmd,
unsigned long arg)
{
long mode=0x00000040;
switch(cmd){
case LED_OFF:
writel(mode, pio_base + PIO_SODR);
printk(KERN_INFO " LED_OFF\n");
break;
case LED_ON:
writel(mode, pio_base + PIO_CODR);
printk(KERN_INFO " LED_ON\n");
break;
default: return -EINVAL;break;
}
return 0;
}
static struct file_operations led_fops =
{
.owner = THIS_MODULE,
.open = baite_led_open,
.ioctl = baite_led_ioctl,
};
struct led_dev *at91_led;
static int led_init(void)
{
int result,err;
printk(DEV_NAME " led_init\n");
at91_led = kmalloc(sizeof(struct led_dev), GFP_KERNEL);
if( DEV_MAJOR != 0){ /*静态申请设置号*/
at91_led->devno = MKDEV(DEV_MAJOR,DEV_MINOR);
result = register_chrdev_region(at91_led->devno,1,DEV_NAME);
at91_led->major = DEV_MAJOR;
at91_led->minor = DEV_MINOR;
}else {
result = alloc_chrdev_region(&at91_led->devno,DEV_MINOR,1,DEV_NAME); /*动态申请设置号*/
at91_led->major = MAJOR(at91_led->devno);
at91_led->minor = DEV_MINOR;
}
if(result < 0){
printk(KERN_ERR"led:can't get major\n");
kfree(at91_led);
return result;
}
cdev_init(&at91_led->cdev,&led_fops); /*设置注册 初始化设备*/
at91_led->cdev.owner = THIS_MODULE;
at91_led->cdev.ops = &led_fops;
err = cdev_add(&at91_led->cdev,at91_led->devno,1);
if(err){
printk(KERN_NOTICE"error %d adding led\n",err);
}
at91_led->class = class_create(THIS_MODULE, DEV_NAME); /*创建一个类*/
if( IS_ERR(at91_led->class) ){ /*IS_ERR() PTR_ERR()等函数是专门用来处理CLASS之类的返回错误代码 */
return PTR_ERR(at91_led->class);
}
at91_led->class_dev = device_create(at91_led->class, NULL,/*创建在/dev目录下创建相应的设备节点。这样,加载模块的时候,*/
/* 用户空间中的udev会自动响应device_create(…)函数,去/sysfs下寻找对应的类从而创建设备节点。*/
// 第一个参数指定所要创建的设备所从属的类,第二个参数是这个设备的父设备,如果没有就指定为NULL,第三个参数是设备号,第四个参数是设备名称,第五个参数是从设备号
at91_led->devno,
NULL,DEV_NAME);
if( IS_ERR(at91_led->class_dev) ){
return PTR_ERR(at91_led->class_dev);
}
return 0;
}
static void __exit led_exit(void)
{
device_destroy(at91_led->class, at91_led->devno);
class_destroy(at91_led->class); 还定义了class_destroy(…)函数,用于在模块卸载时删除类。
cdev_del(&at91_led->cdev);
unregister_chrdev_region(at91_led->devno,1);
kfree(at91_led);
printk(DEV_NAME " led_exit\n");
}
module_init(led_init);
module_exit(led_exit);
MODULE_AUTHOR("zhd wangyulu");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("at91sam9g20 led");
/*****Kconfig*******************************
config baite_LED
tristate "baite_led"
default m
******************************************/
/****Makeflie********************************
obj-$(CONFIG_baite_LED) +=baite_led.o
******************************************/
2:这也是AT91的led的驱动,只是用的是led的标准函数
#include <linux/mm.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/rtc.h> /* get the user-level API */
#include <linux/list.h>
#include <linux/timer.h>
#include <linux/delay.h>
#include <linux/kdev_t.h>
#include <linux/types.h>
#include <linux/cdev.h>
#include <linux/platform_device.h>
#include <linux/version.h>
#include <linux/miscdevice.h>
#include <linux/delay.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/moduleparam.h>
#include <linux/errno.h>
#include <linux/ioctl.h>
#include <linux/pci.h>
#include <linux/input.h>
#include <linux/serio.h>
#include <linux/platform_device.h>
#include <linux/clk.h>
#include <linux/mutex.h>
#include <linux/gpio.h>
#include <linux/io.h>
#include <linux/pci.h>
#include <linux/input.h>
#include <asm/atomic.h>
#include <asm/unistd.h>
#include <asm/uaccess.h>
#include <asm/mach/irq.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#include <asm/leds.h>
#include <asm/mach-types.h>
#include <mach/at91_pio.h>
#include <mach/gpio.h>
#include <mach/hardware.h>
#include <mach/hardware.h>
//boy
/*
通过测试成功 AT91 的开发板 内核是2.6.27
*/
#define DEVICE_NAME "led" /* 加载模式后,执行”cat /proc/devices”命令看到的设备名称 */
#define LED_MAJOR 231 /* */
#define IOCTL_LED_ON 0 /* 应用程序执行ioctl(fd, cmd, arg)时的第2个参数 */
#define IOCTL_LED_OFF 1
#define LED0 AT91_PIN_PA6
static unsigned long led_table [] = { /* 用来指定LED所用的GPIO引脚 */
AT91_PIN_PA6,
AT91_PIN_PA6
};
//应用程序对设备文件/dev/leds执行open(...)时,就会调用s3c2410_leds_open函数
static int S5PC100_leds_open(struct inode *inode, struct file *file)
{
printk(DEVICE_NAME " S5PC100_LEDS_OPEN \n");
at91_set_gpio_output(AT91_PIN_PA6, 1); /* I/O 口设置*/
at91_set_deglitch(AT91_PIN_PA6, 1);
printk(DEVICE_NAME " LEDS_OPEN_END\n");
return 0;
}
//应用程序对设备文件/dev/leds执行ioclt(...)时,就会调用s3c2410_leds_ioctl函数
static int S5PC100_leds_ioctl(
struct inode *inode,
struct file *file,
unsigned int cmd,
unsigned long arg)
{
printk(DEVICE_NAME"leds_ioctl\n");
if (arg > 4) {
return -EINVAL;
}
switch(cmd) {
case IOCTL_LED_ON:
// 设置指定引脚的输出电平为0
printk(DEVICE_NAME" IOCTL_LED_ON\n");
at91_set_gpio_value(led_table [arg], 0);
printk(DEVICE_NAME" LED1_ON\n");
return 0;
case IOCTL_LED_OFF:
printk(DEVICE_NAME" IOCTL_LED_OFF\n");
at91_set_gpio_value(led_table [arg], 1);
printk(DEVICE_NAME" LED1_OFF\n");
return 0;
default:
printk(DEVICE_NAME" leds_ioctl_default\n");
return -EINVAL;
}
}
//字符设备驱动程序的核心,当应用程序操作设备文件时所调用的open、read、write等函数,最终会调用这个>结构中指定的对应函数
static struct file_operations S5PC100_leds_fops = {
.owner = THIS_MODULE,
.open = S5PC100_leds_open,
.ioctl = S5PC100_leds_ioctl,
};
/*static struct miscdevice misc = { //使用与2.6.29的内核 不是使用与2.6.27的内核
.minor = MISC_DYNAMIC_MINOR,
.name = DEVICE_NAME,
.fops = &S5PC100_leds_fops,
};
*/
static int S5PC100_leds_init(void)
{
printk(DEVICE_NAME "S5PC100_LEDS_INIT\n");
int ret;
//ret = misc_register(&misc); //使用与2.6.29的内核 不是使用与2.6.27的内核
ret = register_chrdev(LED_MAJOR, DEVICE_NAME, &S5PC100_leds_fops);
printk(DEVICE_NAME " S5PC100_LEDS_INIT_END\n");
return ret;
}
static void S5PC100_leds_exit(void)
{
//misc_deregister(&misc); //使用与2.6.29的内核 不是使用与2.6.27的内核
unregister_chrdev(LED_MAJOR, DEVICE_NAME);
printk(DEVICE_NAME "s5pc100_leds_exit\n");
}
module_init(S5PC100_leds_init); /* 指定驱动程序的初始化函数和卸载函数 */
module_exit(S5PC100_leds_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("luckyboy");
//表示在make menuconfig 里面可以选着
/*****Kconfig****************************
config BUTTONS
tristate "buttons"
default m
******************************************/
/****Makeflie*****************************
obj-$(CONFIG_BUTTONS) +=at91sam9g20_buttons.o
******************************************/
//表示编译为模块
/****Makeflie*****************************
obj-m +=leds.o
******************************************/
3: 对驱动的应用测试就是;
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/ioctl.h>
/成功的测试程序/
int main(int argc, char **argv)
{
int on;
int led_no;
int fd;
if(argc != 3 ||sscanf(argv[1],"%d",&led_no) !=1 ||
sscanf(argv[2],"%d",&on) !=1 ||
on < 0 || on > 1 || led_no <0 || led_no>3)
{
fprintf(stderr,"Usage:Leds led_no 0|1\n");
exit(1);
}
//打开/dev/leds0设备文件
// fd=open("/dev/leds0",0);
// if(fd<0)
// {
//fd = open("/dev/leds",0);// ceshi leds
fd = open("/dev/led",0);// ceshi led
perror("open device led success");
// perror("open device leds success");
//}
//if(fd <0 )
//{
// perror("open device leds");
// exit(1);
// }
//通过系统调用ioctl和输入的参数控制led
ioctl(fd,on,led_no);
perror("open device led success ioctl");
//关闭设备句柄
close(fd);
return 0;
}
/*
int main(void)
{
int on=1;
int led;
int fd;
//打开/dev/leds0设备文件
fd = open("/dev/leds",0);
if(fd <0 )
{
perror("open device leds");
exit(1);
}
printf( " led_test_show press ctrl+c to exit\n");
//通过系统调用ioctl和输入的参数控制led
while(1)
{
for(led=0;led<4;led++)
{
ioctl(fd, on, led);
usleep(50000);
}
on=!on;
}
//关闭设备句柄
close(fd);
perror("led test ");
return 0;
}
*/
以上是我自己学习驱动的时候写的,现在贴出来,希望对初学者有一点帮助!