混杂设备 LED驱动程序分析
/*******************************
*
*杂项设备驱动:miscdevice
*majior=10;
*
* *****************************/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/types.h>
#include <linux/init.h>
//#include <linux/moduleparam.h>//#include <linux/slab.h>//kcalloc,kzalloc等内存分配函数
//---------ioctl------------
#include <linux/ioctl.h>
//---------misc_register----
#include <linux/miscdevice.h>
//----------cdev--------------
#include <linux/cdev.h>
//----------delay-------------
#include <linux/delay.h>
//----------GPIO---------------
#include <mach/gpio.h>
#include <mach/regs-gpio.h>
#include <plat/gpio-cfg.h>
#define S3C_GPIO_SPECIAL_MARK (0xfffffff0)
#define S3C_GPIO_SPECIAL(x) (S3C_GPIO_SPECIAL_MARK | (x))//内核当中定义
#define DEVICE_NAME "leds"
static int led_gpios[] = {
S5PV210_MP04(4),
S5PV210_MP04(5),
S5PV210_MP04(6),
S5PV210_MP04(7),
};//4个LED
#define LED_NUM ARRAY_SIZE(led_gpios)
static long fl210_leds_ioctl(struct file *filp, unsigned int cmd,
unsigned long arg)
{
switch(cmd) {
case 0:
case 1:
if (arg > LED_NUM) {
return -EINVAL;
}
gpio_set_value(led_gpios[arg], !cmd);//根据cmd设置LED的暗灭
printk(DEVICE_NAME": %ld %d ", arg, cmd);
break;
default:
return -EINVAL;
}
return 0;
}
static struct file_operations fl210_led_dev_fops = {
.owner = THIS_MODULE,
.unlocked_ioctl = fl210_leds_ioctl,
};
//----------------miscdevice------------------static struct miscdevice fl210_led_dev = {
.minor = MISC_DYNAMIC_MINOR,
.name = DEVICE_NAME,
.fops = &fl210_led_dev_fops,
};//--------------------------------------------
static int __init fl210_led_dev_init(void) {
int ret;
int i;
for (i = 0; i < LED_NUM; i++) {
ret = gpio_request(led_gpios[i], "LED");//申请GPIO口
if (ret) {
printk("%s: request GPIO %d for LED failed, ret = %d ", DEVICE_NAME,
led_gpios[i], ret);
return ret;
}
s3c_gpio_cfgpin(led_gpios[i], S3C_GPIO_OUTPUT);//设置GPIO口为输出
gpio_set_value(led_gpios[i], 1);//初始化GPIO口的值
}
ret = misc_register(&fl210_led_dev);//注册杂项设备
printk(DEVICE_NAME" initialized ");
printk("led num is: %d ",LED_NUM);
return ret;
}
static void __exit fl210_led_dev_exit(void) {
int i;
for (i = 0; i < LED_NUM; i++) {
gpio_free(led_gpios[i]);//释放GPIO口
}
misc_deregister(&fl210_led_dev);//注销设备
}
module_init(fl210_led_dev_init);
module_exit(fl210_led_dev_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("");
S5PV210_MP04宏定义在linux/arch/arm/mach-s5pv210/include/mach/gpio.h
#define S5PV210_MP04(_nr) (S5PV210_GPIO_MP04_START + (_nr))
S5PV210_GPIO_MP04_START = S5PV210_GPIO_NEXT(S5PV210_GPIO_MP03),
#define S5PV210_GPIO_NEXT(__gpio) ((__gpio##_START) + (__gpio##_NR) + CONFIG_S3C_GPIO_SPACE + 1)
//这里的CONFIG_S3C_GPIO_SPAC是内核配置选项,在.config中可以找到,我的配置为: CONFIG_S3C_GPIO_SPACE = 0
上述代码用到以下几个函数:
gpio_set_value();
s3c_gpio_cfgpin();
gpio_request();
gpio_free();
misc_register();
misc_deregister();
后面会逐个分析。
测试程序如下:
#include <stdio.h>//#include "sys/types.h"
#include <sys/ioctl.h>
#include <stdlib.h>
#include <unistd.h>//read,write等等//#include "termios.h"//#include "sys/stat.h"
#include <fcntl.h>
#define LED2_ON 0x1#define LED2_OFF 0x0
main(int argc,char *argv[])
{
int fd;
if ((fd=open("/dev/leds",O_RDWR /*| O_NDELAY | O_NOCTTY*/)) < 0)
{
printf("Open Device failed. ");
exit(1);
}
else
{
printf("Open Device successed. ");
}
if (argc<3)
{
/* code */
printf("Usage: %s <on|off num> ",argv[0]);
exit(1);
}
if(!strcmp(argv[1],"on"))
{
printf("led1 will on!! ");
if(ioctl(fd,LED2_ON,atoi(argv[2]))<0)
{
printf("ioctl err!! ");
}
}
if(!strcmp(argv[1],"off"))
{
printf("led1 will off!! ");
if(ioctl(fd,LED2_OFF,atoi(argv[2]))<0)
{
printf("ioctl err!! ");
}
}
close(fd);
}
首先来看s3c_gpio_cfgpin();
int s3c_gpio_cfgpin(unsigned int pin, unsigned int config)
{
struct s3c_gpio_chip *chip = s3c_gpiolib_getchip(pin);//得到对应GPIO结构体首指针,里面包含了该GPIO的各种参数
unsigned long flags;
int offset;
int ret;
if (!chip)
return -EINVAL;
offset = pin - chip->chip.base;
s3c_gpio_lock(chip, flags);
ret = s3c_gpio_do_setcfg(chip, offset, config);//设置该GPIO状态寄存器的数值为config
s3c_gpio_unlock(chip, flags);
return ret;
}
static inline struct s3c_gpio_chip *s3c_gpiolib_getchip(unsigned int chip)
{
return (chip < S3C_GPIO_END) ? s3c_gpios[chip] : NULL;
}
static inline int s3c_gpio_do_setcfg(struct s3c_gpio_chip *chip,
unsigned int off, unsigned int config)
{
return (chip->config->set_config)(chip, off, config);
}
static struct s3c_gpio_cfg gpio_cfg = {
.set_config = s3c_gpio_setcfg_s3c64xx_4bit,
.set_pull = s3c_gpio_setpull_updown,
.get_pull = s3c_gpio_getpull_updown,
};
int s3c_gpio_setcfg_s3c64xx_4bit(struct s3c_gpio_chip *chip,
unsigned int off, unsigned int cfg)
{
void __iomem *reg = chip->base;
unsigned int shift = (off & 7) * 4;
u32 con;
if (off < 8 && chip->chip.ngpio > 8)
reg -= 4;
if (s3c_gpio_is_cfg_special(cfg)) {
cfg &= 0xf;
cfg <<= shift;
}
con = __raw_readl(reg);//读GPXCON的值
con &= ~(0xf << shift);//清零
con |= cfg;//设置config
__raw_writel(con, reg);//写值
return 0;
}
其中结构体s3c_gpio_chip如下:
*/struct s3c_gpio_chip {
struct gpio_chip chip;
struct s3c_gpio_cfg *config;
struct s3c_gpio_pm *pm;
void __iomem *base;
int eint_offset;
spinlock_t lock;
#ifdef CONFIG_PM
u32 pm_save[7];#endif
};
static struct s3c_gpio_chip s5pv210_gpio_4bit[] = {//描述了芯片中所有的GPIO端口
{
.chip = {
.base = S5PV210_GPA0(0),
.ngpio = S5PV210_GPIO_A0_NR,
.label = "GPA0",
.to_irq = s5p_gpiolib_gpioint_to_irq,
},
},{
。。。。
}
。。。。
};
接下来看gpio_set_value();
void __gpio_set_value(unsigned gpio, int value)
{
struct gpio_chip *chip;
chip = gpio_to_chip(gpio);//返回对应于pin的gpio_desc[pin].chip指针 WARN_ON(extra_checks && chip->can_sleep);
chip->set(chip, gpio - chip->base, value);//调用chip->set
}