分类: LINUX
2009-05-01 23:49:21
以下是扬创开发板给的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 [] = {
S
S
S
S
};
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;
}
s
/*给寄存器赋值,使某一位置1或0,led亮,给寄存器置0,因此对cmd取反*/
/*通过s
/*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"<6>" 提示信息,如驱动程序启动时,打印硬件信息
没有指定日志级别的printk语句默认采用的级别是 DEFAULT_ MESSAGE_LOGLEVEL
(这个默认级别一般为<4>,即与KERN_WARNING在一个级别上),
其定义在linux26/kernel/printk.c中可以找到。
下面是一个比较简单的使用
printk(KERN_INFO "INFO\n"); //这里可以使用数字代替 KERN_INFO,
即可以写成printk(<6> "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
#include
#include
#include
/*执行:./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 < 0 || on > 1 || led_number < 0 || 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 < 0) {
perror("open device /dev/led");
exit(1);
}
ioctl(fd, on, led_number);
close(fd);
return 0;
}
总结:
上面是点亮led的应用程序和驱动程序,在应用程序中,可以根据需要进行修改,比如让led闪烁等等,驱动程序和应用程序分别给了详细注释,如有错误欢迎指正!程序已在扬创开发板上验证,但验证中发现第二个led不亮,还不知为什么,望高手指点!
chinaunix网友2010-03-20 11:42:34
/dev/led的设备名是通过mknod生成的,此驱动是静态加载的,如果是按模块加载,方法如下: insmod led mknod /de/led c 243 0 欢迎讨论!
pan_1304212010-03-19 18:10:36
请教一下, /dev/led节点是在 misc_register(&led_dev);调用中生成的吗? 这是不是mknod命令的代码实现? 一般的char设备注册还可以用什么函数呢? 谢谢。