字符设备驱动之led灯的控制实验

本文深入探讨了Linux设备驱动开发的全过程,从初始化到退出,包括注册设备、内存分配、设备节点创建、文件操作集关联及设备添加到系统中等关键步骤。详细展示了如何通过宏定义和函数实现GPIO配置、打开和关闭LED灯的功能。
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/poll.h>
#include <linux/irq.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <linux/interrupt.h>
#include <asm/uaccess.h>
#include <linux/platform_device.h>
#include <linux/cdev.h>
#include <linux/miscdevice.h>
#include <linux/slab.h>
#define DEVICE_NAME "leds"
#define  GPIOCFG1  (*(volatile unsigned long*) 0xbfd010C4) 
#define  GPIOOE1   (*(volatile unsigned long*)0xbfd010D4)   
#define  GPIOOUT1  (*(volatile unsigned long*)0xbfd010F4)  
static struct led_dev {
	dev_t devno;
	struct cdev cdev;	
};

struct led_dev *leds = NULL;

static int leds_open(struct inode *inode, struct file *file)
{
    printk(KERN_WARNING "open led linght\n");
    GPIOCFG1 |= 0x000000C0;
    GPIOOE1 &= ~(0x000000C0);
    GPIOOUT1 &= ~(0x000000C0);   
    return 0;
}

/*static int leds_close(struct inode *inode, struct file *file)
{
    
    printk(KERN_WARNING "close\n");
    return 0;
}*/

static int leds_ioctl(struct inode *inode, struct file *file,
        unsigned int cmd, unsigned long arg)
{
    return 0;
}

static struct file_operations dev_fops = {
    .owner	= THIS_MODULE,
    .open	= leds_open,
    .unlocked_ioctl	= leds_ioctl,
   /* .release	= leds_close,*/
};

static int __init dev_init(void)/*驱动的初始化*/
{
    int ret;
	dev_t devno=MKDEV(252,0);

    ret = register_chrdev_region(devno,1,"leds");/*静态注册设备号*/
	if(ret < 0){
		printk(KERN_WARNING "leds:can't get major.\n");
		goto fail;
	}

	leds = kmalloc(sizeof(struct led_dev), GFP_KERNEL);	/*设备节点分配内存空间*/
	if(!leds){
		ret = -ENOMEM;
		goto fail;
	}
	memset(leds, 0, sizeof(struct led_dev));
	leds->devno = devno;

	cdev_init(&leds->cdev, &dev_fops);/*关联操作集*/
	leds->cdev.owner = THIS_MODULE;
	ret = cdev_add(&leds->cdev, devno, 1);/*将设备加入到系统中*/
	if(ret){
		printk(KERN_NOTICE "Error %d.\n", ret);
		goto fail;
	}
	
    printk ("led driver initialized.\n");
    return 0;

fail:
	if(devno != 0){
		unregister_chrdev_region(&devno, 1);
	}
	if(leds){
		kfree(leds);
	}
	return ret;
}
static void __exit dev_exit(void)/*释放设备节点*/
{
	dev_t devno = leds->devno;

	cdev_del(&leds->cdev);
	if(leds){
		kfree(leds);
	}
	unregister_chrdev_region(devno, 1);
}

module_init(dev_init);
module_exit(dev_exit);
MODULE_LICENSE("GPL");

### 嵌入式系统中字符设备驱动的开发与控制方法 在嵌入式Linux系统中,字符设备驱动是一种常见的外设控制方式。字符设备驱动通过文件操作接口(如`open`、`read`、`write`和`ioctl`)为用户提供访问硬件资源的能力。以下是字符设备驱动开发的关键步骤及其实验示例。 #### 1. 字符设备驱动的基本架构 字符设备驱动的核心在于实现文件操作结构体`file_operations`,并将其注册到内核中。该结构体定义了设备支持的操作函数,例如读写和控制命令。以下是一个典型的字符设备驱动框架[^2]: ```c #include <linux/module.h> #include <linux/kernel.h> #include <linux/fs.h> #include <linux/uaccess.h> #define DEVICE_NAME "my_char_dev" #define BUF_LEN 80 static int major_number; static char message[BUF_LEN]; static short size_of_message; // 文件操作函数 static ssize_t dev_read(struct file *filp, char __user *buffer, size_t length, loff_t *offset) { int bytes_read = 0; if (size_of_message == 0) return 0; while (length && size_of_message) { put_user(message[bytes_read], buffer + bytes_read); size_of_message--; length--; bytes_read++; } return bytes_read; } static ssize_t dev_write(struct file *filp, const char __user *buffer, size_t length, loff_t *offset) { size_of_message = length > BUF_LEN ? BUF_LEN : length; copy_from_user(message, buffer, size_of_message); return size_of_message; } static int dev_open(struct inode *inode, struct file *file) { printk(KERN_INFO "Device has been opened\n"); return 0; } static int dev_release(struct inode *inode, struct file *file) { printk(KERN_INFO "Device successfully closed\n"); return 0; } static struct file_operations fops = { .owner = THIS_MODULE, .read = dev_read, .write = dev_write, .open = dev_open, .release = dev_release, }; static int __init chardev_init(void) { major_number = register_chrdev(0, DEVICE_NAME, &fops); if (major_number < 0) { printk(KERN_ALERT "Failed to register a major number\n"); return major_number; } printk(KERN_INFO "I was assigned major number %d. To talk to\n", major_number); return 0; } static void __exit chardev_exit(void) { unregister_chrdev(major_number, DEVICE_NAME); printk(KERN_INFO "Goodbye from the LKM!\n"); } module_init(chardev_init); module_exit(chardev_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Author Name"); MODULE_DESCRIPTION("A simple Linux char driver"); ``` #### 2. 控制LED字符设备驱动示例 结合上述代码框架,可以进一步扩展以实现对LED控制。以下是一个基于GPIO子系统的字符设备驱动示例[^4]: ```c #include <linux/gpio.h> #include <linux/init.h> #include <linux/module.h> #include <linux/fs.h> #include <linux/cdev.h> #include <linux/uaccess.h> #define LED_PIN 17 // GPIO引脚号 #define DEVICE_NAME "led_driver" static int led_major = 0; static struct cdev led_cdev; static int led_open(struct inode *inode, struct file *file) { gpio_request(LED_PIN, "sysfs"); // 请求GPIO gpio_direction_output(LED_PIN, 0); // 设置为输出模式 gpio_export(LED_PIN, false); // 导出到用户空间 return 0; } static int led_release(struct inode *inode, struct file *file) { gpio_set_value(LED_PIN, 0); // 熄LED gpio_unexport(LED_PIN); // 取消导出 gpio_free(LED_PIN); // 释放GPIO return 0; } static ssize_t led_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { int value; if (copy_from_user(&value, buf, sizeof(int))) return -EFAULT; if (value) gpio_set_value(LED_PIN, 1); // 点LED else gpio_set_value(LED_PIN, 0); // 熄LED return count; } static struct file_operations led_fops = { .owner = THIS_MODULE, .open = led_open, .release = led_release, .write = led_write, }; static int __init led_init(void) { int ret; dev_t dev_num; ret = alloc_chrdev_region(&dev_num, 0, 1, DEVICE_NAME); if (ret < 0) { printk(KERN_ERR "Failed to allocate character device region\n"); return ret; } cdev_init(&led_cdev, &led_fops); led_cdev.owner = THIS_MODULE; ret = cdev_add(&led_cdev, dev_num, 1); if (ret < 0) { unregister_chrdev_region(dev_num, 1); printk(KERN_ERR "Failed to add character device\n"); return ret; } led_major = MAJOR(dev_num); printk(KERN_INFO "LED Driver: Registered with major number %d\n", led_major); return 0; } static void __exit led_exit(void) { cdev_del(&led_cdev); unregister_chrdev_region(MKDEV(led_major, 0), 1); printk(KERN_INFO "LED Driver: Unregistered\n"); } module_init(led_init); module_exit(led_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Author Name"); MODULE_DESCRIPTION("LED Control via Character Device"); ``` #### 3. 用户空间控制 在用户空间中,可以通过`echo`命令或编写C程序来控制LED。例如,使用`echo`命令点和熄LED[^2]: ```bash echo 1 > /dev/led_driver # 点LED echo 0 > /dev/led_driver # 熄LED ``` 或者编写一个简单的C程序进行控制: ```c #include <stdio.h> #include <fcntl.h> #include <unistd.h> int main() { int fd = open("/dev/led_driver", O_RDWR); if (fd < 0) { perror("Failed to open device"); return -1; } int value = 1; // 点LED write(fd, &value, sizeof(value)); sleep(2); value = 0; // 熄LED write(fd, &value, sizeof(value)); close(fd); return 0; } ``` #### 4. 总结 字符设备驱动是嵌入式Linux系统中一种重要的外设控制方式。通过实现`file_operations`结构体中的函数,并将设备注册到内核中,可以为用户提供文件操作接口。以上代码示例展示了如何通过字符设备驱动控制LED,包括驱动程序的编写和用户空间的控制方法。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值