Linux 字符驱动程序(一)
Linux 字符驱动程序(一)
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <asm/uaccess.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <asm/arch/regs-gpio.h>
#include <asm/hardware.h>
//相关的头文件,我们仿照其他模块加载即可。
#define DEVICE_NAME "leds" /* 加载模式后,执行”cat /proc/devices”命令看到的设备名称 */
static struct class *leds_class;
static struct class_device * leds_class_devs[4];
volatile unsigned long *gpfcon = NULL;
volatile unsigned long *gpfdat = NULL;
//应用程序执行open时调用该函数;
static int first_drv_open(struct inode *inode, struct file *file)
{
int minor = MINOR(inode->i_rdev); //MINOR(inode->i_cdev);
switch(minor)
{
case 0: /* /dev/leds */
{
*gpfcon &= ~((0x3<<(4*2)) | (0x3<<(5*2)) | (0x3<<(6*2)));
*gpfcon |= ((0x1<<(4*2)) | (0x1<<(5*2)) | (0x1<<(6*2)));
break;
}
case 1: /* /dev/led1 */
{
*gpfcon &= ~((0x3<<(4*2));
*gpfcon |= ((0x1<<(4*2));
break;
}
case 2: /* /dev/led2 */
{
*gpfcon &= ~ (0x3<<(5*2));
*gpfcon |= (0x1<<(5*2));
break;
}
case 3: /* /dev/led3 */
{
*gpfcon &= ~(0x3<<(6*2));
*gpfcon |= (0x1<<(6*2));
break;
}
}
return 0;
}
//应用程序执行write时调用该函数;
static ssize_t first_drv_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos)
{
int val;
copy_from_user(&val, buf, count);
switch (minor)
{
case 0: /* /dev/leds */
{
if (val == 1)
{
// 点灯
*gpfdat &= ~((1<<4) | (1<<5) | (1<<6));
}
else
{
// 灭灯
*gpfdat |= (1<<4) | (1<<5) | (1<<6);
}
break;
}
case 1: /* /dev/led1 */
{
if (val == 1)
{
// 点灯
*gpfdat &= ~(1<<4);
}
else
{
// 灭灯
*gpfdat |= (1<<4);
}
break;
}
case 2: /* /dev/led2 */
{
if (val == 1)
{
// 点灯
*gpfdat &= ~(1<<5);
}
else
{
// 灭灯
*gpfdat |= (1<<5);
}
break;
}
case 3: /* /dev/led3 */
{
if (val == 1)
{
// 点灯
*gpfdat &= ~(1<<6);
}
else
{
// 灭灯
*gpfdat |= (1<<6);
}
break;
}
}
return 0;
}
//驱动程序与内核的接口;
static struct file_operations first_drv_fops = {
.owner = THIS_MODULE, /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */
.open = first_drv_open,
.write = first_drv_write,
};
int major; //记录动态获取的设备号
* 执行insmod命令时就会调用这个函数
*/
static int first_drv_init(void)
{
int minor = 0; //次设备号
printk(DEVICE_NAME " can‘t register major number\n");
return major;
}
leds_class = class_create(THIS_MODULE, "leds"); // 产生节点类,以leds_class声明的均为同一种设备
if (IS_ERR(leds_class))
return PTR_ERR(leds_class);
leds_class_devs[0] = class_device_create(leds_class, NULL, MKDEV(major, 0), NULL, "leds");
//产生不同的从设备,并以不同的名字挂接在/dev目录下;
for (minor = 1; minor < 4; minor++)
{
leds_class_devs[minor] = class_device_create(leds_class, NULL, MKDEV(major, minor), NULL, "led%d", minor);
if (unlikely(IS_ERR(leds_class_devs[minor])))
return PTR_ERR(leds_class_devs[minor]);
}
gpfcon = (volatile unsigned long *)ioremap(0x56000050, 16);//控制寄存器地址
gpfdat = gpfcon + 1; // 0x56000054 //数据寄存器地址。
printk(DEVICE_NAME " initialized\n");
return 0;
}
/*
* 执行rmmod命令时就会调用这个函数
*/
static void first_drv_exit(void)
{
int minor;
/* 卸载驱动程序 */
unregister_chrdev(major, DEVICE_NAME);
for (minor = 0; minor < 4; minor++)
{
class_device_unregister(leds_class_devs[minor]);
}
class_destroy(leds_class);
iounmap(gpfcon);
}
module_init(first_drv_init);
module_exit(first_drv_exit);
/* 描述驱动程序的一些信息,不是必须的 */
MODULE_AUTHOR("http://www.100ask.net");
MODULE_VERSION("0.1.0");
MODULE_DESCRIPTION("LED Driver");
MODULE_LICENSE("GPL");
我们写应用程序时,打开相应的设备/dev/leds , /dev/led1, /dev/led2, /dev/led3 向其中写1,0 就可以控制全部led或者某个led的亮灭.
郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。