第一个Linux驱动-s3c6410 gpio
前段时间学着写了第一个linux下的驱动,很简单,基于友善之臂的tiny6410,通过控制GPIOK4-7输入输出来控制板上的4个led。led的驱动友善已经提供,不过我自己写的有些不一样,是按照标准的char驱动来写的,下面是全过程。
注意:此代码基于友善之臂提供的已经移植好的linux2.6.36核心
第一步编写驱动代码
//tiny6410_gpio.c
#include <linux/miscdevice.h>
#include <linux/delay.h>
#include <asm/irq.h>
//#include <mach/regs-gpio.h>
#include <mach/hardware.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/mm.h>
#include <linux/fs.h>
#include <linux/types.h>
#include <linux/delay.h>
#include <linux/moduleparam.h>
#include <linux/slab.h>
#include <linux/errno.h>
#include <linux/ioctl.h>
#include <linux/cdev.h>
#include <linux/string.h>
#include <linux/list.h>
#include <linux/pci.h>
#include <asm/uaccess.h>
#include <asm/atomic.h>
#include <asm/unistd.h>
#include <mach/map.h>
#include <mach/regs-clock.h>
#include <mach/regs-gpio.h>
#include <plat/gpio-cfg.h>
#include <mach/gpio-bank-e.h>
#include <mach/gpio-bank-k.h>
#define DEVICE_NAME "tiny6410_gpio" //驱动名字
#define DEVICE_MAJOR 100 //主设备号
static int tiny6410_gpio_major=DEVICE_MAJOR;
//自己定义的描述gpio的结构体
struct tiny6410_gpio_dev
{
struct cdev cdev;//描述字符型设备的cdev结构体
unsigned *conf0;
unsigned *conf1;
unsigned *data;
unsigned *pud;
};
//初始化dev内的寄存器地址
struct tiny6410_gpio_dev dev=
{
.conf0=S3C64XX_GPKCON,
.conf1=S3C64XX_GPKCON1,
.data=S3C64XX_GPKDAT,
.pud=S3C64XX_GPKPUD,
};
//这个驱动仅实现read和write函数,一个字节代表一个灯的状态
ssize_t tiny6410_gpio_read (struct file *filp, char __user *buf_user, size_t size, loff_t *f_pos)
{
unsigned val,i;
val=size;
i=*f_pos;
printk("start read start:%x size:%d\r\n",i,val);//输出调试信息。很奇怪,直接输出*f_pos和size数值回不对,只能中转一下
if(*f_pos>=4 || size==0)
{
put_user(0,buf_user);
return 0;
}
val=readl(dev.data);
printk("raw val:%x\r\n",val);
val=(val>>(4+*f_pos))&0xf;
for(i=0;i<((size+*f_pos)>4?4-*f_pos:size);i++)
put_user((val>>i)&0x01,buf_user+i);
printk("get val:%x max index:%d\r\n",val,i);
return 0;
}
//ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
ssize_t tiny6410_gpio_write (struct file *filp, const char __user *buf_user, size_t size, loff_t *f_pos)
{
unsigned char writesize,i,data[4];
unsigned raw;
printk("start write start:%x,size:%d",*f_pos,size);
if(*f_pos>=4)
{
return size;
}
writesize=4-copy_from_user(data,buf_user,size);
if(writesize==0)
{
printk("write size 0!\r\n");
return size;
}
raw=readl(dev.data);
printk("source val:%x,write size:%d\r\n",raw,writesize);
for(i=0;i<((writesize+*f_pos)>4?4-*f_pos:writesize);i++)
{
raw&=~(1<<(i+*f_pos+4));
raw|=((data[i]?1:0)<<(i+4+*f_pos));
}
writel(raw,dev.data);
printk("write end! dest val:%x max index:\r\n",raw,i);
return 4-writesize;
}
//这个结构体内的函数是驱动编写主要修改的地方
static struct file_operations dev_fops = {
.owner = THIS_MODULE,
//.unlocked_ioctl = sbc2440_leds_ioctl,
.read = tiny6410_gpio_read,
.write = tiny6410_gpio_write,
};
static int tiny6410_gpio_setup_cdev()
{
int devno=MKDEV(tiny6410_gpio_major,0);
cdev_init(&dev.cdev,&dev_fops);
dev.cdev.owner=THIS_MODULE;
return cdev_add(&dev.cdev,devno,1);//添加一个cdev结构体
}