免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 1144 | 回复: 0
打印 上一主题 下一主题

十一、Linux驱动程序开发(4) - 字符设备驱动(3)-LED设备驱动和应用程序 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2009-05-01 23:49 |只看该作者 |倒序浏览

以下是扬创开发板给的led例程,将对应用程序和驱动程序进行详细注释和分析,并验证!
/*
*  LED  interface   driver for utu2440
* This file is subject to the terms and conditions of the GNU General Public
* License.  See the file "COPYING" in the main directory of this archive
* for more details.
*   bit.lili@gmail.com 2007-6
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include

#define LED_DRIVER       "utu2440 LED Driver v1.00"

static unsigned long led_table [] = {
       S3C2410_GPF4,
       S3C2410_GPF5,
       S3C2410_GPF6,
       S3C2410_GPF7,
};

static int led_ioctl(struct inode *inode, struct file *file,
                   unsigned int cmd, unsigned long arg);
/*ioctl(fd, on, led_number);
inode 和 filp 指针是对应应用程序传递的文件描述符 fd 的值,
和传递给 open 方法的相同参数*/
static int led_ioctl(struct inode *inode, struct file *file,
                   unsigned int cmd, unsigned long arg)
{
       switch (cmd) {
       case 0:
       case 1:
              if (arg > 4) {
                     return -EINVAL;
              }
              s3c2410_gpio_setpin(led_table[arg], !cmd);
/*给寄存器赋值,使某一位置1或0,led亮,给寄存器置0,因此对cmd取反*/
/*通过s3c2410_gpio_setpin()来做,此函数为驱动函数的核心操作*/
/*case 0和case 1操作一样*/
       default:
              return -EINVAL;
       }
       return 0;
}

/*设备驱动文件操作结构体*/
static struct file_operations led_fops = {
       .ioctl = led_ioctl,
};

static struct miscdevice led_dev = {
       MISC_DYNAMIC_MINOR,
       "led",
       &led_fops
};
/*
struct miscdevice  {
       int minor; //MISC_DYNAMIC_MINOR
       const char *name;//"led"
       const struct file_operations *fops; //&led_fops
       struct list_head list;
       struct device *parent;
       struct device *this_device;
};
此结构体是注册混合设备所需要的参数。主要有:
minor:次设备号,所有的misc设备共用一个主设备号,所以注册misc设备时只要次设备号就可以了。利用次设备号来区分设备的。
name:misc设备名。
*fops:misc设备文件操作结构体。其它三个参数很少用
*/

static int led_init(void)
{
       pr_info("%s\n", LED_DRIVER);
       /*printk(KERN_INFO fmt, ##arg)
       #defineKERN_INFO""   提示信息,如驱动程序启动时,打印硬件信息
       没有指定日志级别的printk语句默认采用的级别是 DEFAULT_ MESSAGE_LOGLEVEL
       (这个默认级别一般为,即与KERN_WARNING在一个级别上),
       其定义在linux26/kernel/printk.c中可以找到。
  下面是一个比较简单的使用
  printk(KERN_INFO "INFO\n");  //这里可以使用数字代替 KERN_INFO,
  即可以写成printk( "INFO\n");  
  在这个格式的定义中,日志级别和信息文本之间不能够使用逗号隔开,
  因为系统在进行编译的时候,将日志级别转换成字符串于后面的文本信息进行连接。*/
       misc_register(&led_dev);// misc设备注册
/*非标准设备使用 misc_register,即一些字符设备不符合预先确定的字符设备范畴,
这些设备就用主编号10一起归于"其他类型",misc_register()用主编号10调用 register_chrdev(),设备名称和函数表指针通过miscdevice数据结构获得。同样,miscdevice 数据结构还保存设备驱动程序所使用的次要号码。*/   
       return 0;
}

static void __exit led_exit(void)
{
       misc_deregister(&led_dev);
       /*misc(混合,其他类型,不能严格划分的设备类型)类设备的注销函数,成功返回为0,错误返回一个错误代码*/
}

module_init(led_init);
module_exit(led_exit);

MODULE_AUTHOR("lili         bit.lili@gmail.com");
MODULE_LICENSE("GPL");


应用程序:
#include /*标准输入输出库,像sscanf函数,fprintf函数都是在这个库里*/
#include
#include  /*一些宏的定义在这里面,像stderr*/
#include /*文件操作控制库,像ioctl函数就在这里*/
/*执行:./led 1 1*/
int main(int argc, char **argv)
/*argc表示参数的个数,而参数都存放在argv里,它是指针数组*/
{
       int on; /*led的开关状态,从第三个参数中获取*/
       int led_number; /*led的编号,从第二个参数中获取*/
       int fd; /*设备号,将从打开的leds设备获得*/

/*获取参数,并作参数的检验*/
    /*scanf/sscanf 函数的返回值反映的是按照指定的格式符正确读入的数据的个数
(sscanf(argv[1], "%d", &led_number)返回正确,应等于1)。
如果输入数据与指定格式不符,则会产生输入错误。遇到输入错误,scanf函数会立即终止,返回已经成功读取的数据的个数。所以,通过scanf函数的返回值和指定输入数据的个数(由格式符决定)的比较,可以判断数据输入是否成功。
*/
       if (argc != 3 || sscanf(argv[1], "%d", &led_number) != 1 || sscanf(argv[2],"%d", &on) != 1 ||on  1 || led_number  3) {
              fprintf(stderr, "Usage:\n");
              fprintf(stderr, "\t led led_number on|off\n");
              fprintf(stderr, "Options:\n");
              fprintf(stderr, "\t led_number from 0 to 3\n");
              fprintf(stderr, "\t on 1   off 0\n");
/*stdout -- 标准输出设备 (printf("..")) 同 stdout。
stderr -- 标准错误输出设备
两者默认向屏幕输出。
但如果用转向标准输出到磁盘文件,则可看出两者区别。stdout输出到磁盘文件,stderr在屏幕*/
              exit(1);
       }
       fd = open("/dev/led", 0);
       /*驱动程序可以不实现open这个函数,在这种情况下,设备的打开操作永远成功。*/
       if (fd
              perror("open device /dev/led");
              exit(1);
       }
       ioctl(fd, on, led_number);
       close(fd);
       return 0;
}

总结:
上面是点亮led的应用程序和驱动程序,在应用程序中,可以根据需要进行修改,比如让led闪烁等等,驱动程序和应用程序分别给了详细注释,如有错误欢迎指正!程序已在扬创开发板上验证,但验证中发现第二个led不亮,还不知为什么,望高手指点!


本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u3/94284/showart_1916006.html
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP