主题 : LINUX2.6新型字符驱动开发实例 复制链接 | 浏览器收藏 | 打印
级别: 新手上路
UID: 53
精华: 0
发帖: 26
金钱: 155 两
威望: 63 点
综合积分: 52 分
注册时间: 2008-01-30
最后登录: 2016-10-18
楼主  发表于: 2010-01-04 17:39

 LINUX2.6新型字符驱动开发实例

东哥的书非常好,我就靠这本书入门的,感谢他一下。不过DIY的人不仅仅是学习而是超越,我在研究2.6的驱动的时候发现东哥开发字符驱动的时候还用的老模式的驱动,今天我就试着改变一下。为了让大家好学,我利用书上的版本修改,好对比。代码如下:
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <asm/irq.h>
#include <mach/regs-gpio.h>
#include <mach/hardware.h>
#include <linux/cdev.h>//新字符结构头文件
#include <linux/kdev_t.h>//设备号头文件

#define DEVICE_NAME    "leds"  /* 加载模式后,执行”cat /proc/devices”命令看到的设备名称 */
//#define LED_MAJOR      231    /* 主设备号 */
int major =231;
int minor =0;
/* 应用程序执行ioctl(fd, cmd, arg)时的第2个参数 */
#define IOCTL_LED_ON    0
#define IOCTL_LED_OFF  1

struct cdev s3c24xx_leds_cdev;//初始化一个新字符驱动的结构体

/* 用来指定LED所用的GPIO引脚 */
static unsigned long led_table [] = {
    S3C2410_GPB5,
    S3C2410_GPB6,
    S3C2410_GPB7,
    S3C2410_GPB8,
};
/* 用来指定GPIO引脚的功能:输出 */
static unsigned int led_cfg_table [] = {
    S3C2410_GPB5_OUTP,
    S3C2410_GPB6_OUTP,
    S3C2410_GPB7_OUTP,
    S3C2410_GPB8_OUTP,
};
/* 应用程序对设备文件/dev/leds执行open(...)时,
* 就会调用s3c24xx_leds_open函数
*/
static int s3c24xx_leds_open(struct inode *inode, struct file *file)
{
    int i;
    
    for (i = 0; i < 4; i++) {
        // 设置GPIO引脚的功能:本驱动中LED所涉及的GPIO引脚设为输出功能
        s3c2410_gpio_cfgpin(led_table, led_cfg_table);
    }
    return 0;
}
/* 应用程序对设备文件/dev/leds执行ioclt(...)时,
* 就会调用s3c24xx_leds_ioctl函数
*/
static int s3c24xx_leds_ioctl(
    struct inode *inode,
    struct file *file,
    unsigned int cmd,
    unsigned long arg)
{
    if (arg > 4) {
        return -EINVAL;
    }
    
    switch(cmd) {
    case IOCTL_LED_ON:
        // 设置指定引脚的输出电平为0
        s3c2410_gpio_setpin(led_table[arg], 0);
        return 0;
    case IOCTL_LED_OFF:
        // 设置指定引脚的输出电平为1
        s3c2410_gpio_setpin(led_table[arg], 1);
        return 0;
    default:
        return -EINVAL;
    }
}
/* 这个结构是字符设备驱动程序的核心
* 当应用程序操作设备文件时所调用的open、read、write等函数,
* 最终会调用这个结构中指定的对应函数
*/
static struct file_operations s3c24xx_leds_fops = {
    .owner  =  THIS_MODULE,    /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */
    .open  =  s3c24xx_leds_open,    
    .ioctl  =  s3c24xx_leds_ioctl,
};
/*
* 执行“insmod s3c24xx_leds.ko”命令时就会调用这个函数
*/
static int __init s3c24xx_leds_init(void)
{
    int err;
    /* 注册字符设备驱动程序
    * 参数为主设备号、设备名字、file_operations结构;
    * 这样,主设备号就和具体的file_operations结构联系起来了,
    * 操作主设备为LED_MAJOR的设备文件时,就会调用s3c24xx_leds_fops中的相关成员函数
    * LED_MAJOR可以设为0,表示由内核自动分配主设备号
    */
/*
--  ret = register_chrdev(LED_MAJOR, DEVICE_NAME, &s3c24xx_leds_fops);
--  if (ret < 0) {
--    printk(DEVICE_NAME " can't register major number\n");
  --    return ret;
--  }
    */
      int devno = MKDEV(major,minor);//新的字符型驱动设备号需要是DEV_T的类型,这里转换合成一下
    cdev_init(&s3c24xx_leds_cdev, &s3c24xx_leds_fops);//初始化字符设备结构体和fops结构体
    s3c24xx_leds_cdev.ops = &s3c24xx_leds_fops;//填充必要的结构体成员建立关联
    s3c24xx_leds_cdev.owner = THIS_MODULE;
    err = cdev_add (&s3c24xx_leds_cdev,devno,1);//注册设备
    if(err<0)
    {printk(DEVICE_NAME " can't register major number\n");
      return err;
                      }
    printk(DEVICE_NAME " initialized\n");
    return 0;
}
/*
* 执行”rmmod s3c24xx_leds.ko”命令时就会调用这个函数
*/
static void __exit s3c24xx_leds_exit(void)
{
    /* 卸载驱动程序 */
  // unregister_chrdev(LED_MAJOR, DEVICE_NAME);
      cdev_del(&s3c24xx_leds_cdev);//删除设备
}
/* 这两行指定驱动程序的初始化函数和卸载函数 */
module_init(s3c24xx_leds_init);
module_exit(s3c24xx_leds_exit);
/* 描述驱动程序的一些信息,不是必须的 */
MODULE_AUTHOR("http://www.100ask.net");            // 驱动程序的作者
MODULE_DESCRIPTION("S3C2410/S3C2440 LED Driver");  // 一些描述信息
MODULE_LICENSE("GPL");                              // 遵循的协议
2.6的字符驱动是以后的必然,老的方式早晚要取消。。。。。。。。。。。欢迎大家讨论:QQ:101859673