一个先前在2440板子上跑过的cdev字符设备的例子。整理一下
mygpio.c
#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/ioport.h>
#include <asm/io.h>
#include "mygpio.h"
MODULE_LICENSE("GPL");
MODULE_AUTHOR("zl");
/*
*注: 调用request_mem_region()不是必须的,但是建议使用。该函数的任务是检查申请的资源是否可用,如果可用则申请成功,并标志为已经使用,其他驱动想再申请该资源时就会失败。
*/
struct mygpio_device {
unsigned long io_phys, io_virt;
volatile unsigned long *gpfcon, *gpfdat, *gpfup;
void (*on)(struct mygpio_device *);
void (*off)(struct mygpio_device *);
struct cdev dev;
dev_t no;
};
void led_on(struct mygpio_device* mydev)
{
*(mydev->gpfcon) &= ~(0x3 << 8);
*(mydev->gpfcon) |= (0x1 << 8);
*(mydev->gpfup) |= (0x1 << 4);
*(mydev->gpfdat) &= ~(0x1 << 4);
}
void led_off(struct mygpio_device* mydev)
{
*(mydev->gpfcon) &= ~(0x3 << 8);
*(mydev->gpfcon) |= (0x1 << 8);
*(mydev->gpfup) |= (0x1 << 4);
*(mydev->gpfdat) |= (0x1 << 4);
}
int init_gpiodev(struct mygpio_device* mydev)
{
int ret = 0;
struct resource *res = NULL;
mydev->io_phys = 0x56000000;
res = request_mem_region(mydev->io_phys, SZ_4K, "mygpio device");
if(NULL == res)
{
ret = -EBUSY;
printk("request_mem_region failed!\n");
return ret;
}
mydev->io_virt = (unsigned long)ioremap(mydev->io_phys, SZ_4K);
mydev->gpfcon = (volatile unsigned long *)(mydev->io_virt + 0x50);
mydev->gpfdat = (volatile unsigned long *)(mydev->io_virt + 0x54);
mydev->gpfup = (volatile unsigned long *)(mydev->io_virt + 0x58);
mydev->on = led_on;
mydev->off = led_off;
return ret;
}
void destory_gpiodev(struct mygpio_device* mydev)
{
release_mem_region(mydev->io_phys, SZ_4K);
iounmap((void*)mydev->io_virt);
mydev->on = NULL;
mydev->off = NULL;
}
struct mygpio_device my_dev;
ssize_t my_write(struct file *fp, const char __user *buf, size_t count, loff_t *off)
{
return 0;
}
ssize_t my_read(struct file *fp, char __user *buf, size_t count, loff_t *off)
{
return 0;
}
ssize_t my_ioctl(struct file *fp, unsigned int cmd, unsigned long arg)
{
int ret;
struct mygpio_device *mydev = fp->private_data;
switch(cmd) {
case LED_ON:
mydev->on(mydev);
break;
case LED_OFF:
mydev->off(mydev);
break;
default:
ret = -EINVAL;
break;
}
return 0;
}
loff_t my_lseek (struct file *fp, loff_t offset, int whence)
{
return 0;
}
int my_open(struct inode *no, struct file *fp)
{
//Note: How did the open function get the device struct.
fp->private_data = container_of(no->i_cdev, struct mygpio_device, dev);
return 0;
}
int my_release(struct inode *no, struct file *fp)
{
return 0;
}
struct file_operations my_ops = {
.open = my_open,
.release = my_release,
.read = my_read,
.write = my_write,
.unlocked_ioctl = my_ioctl,
.llseek = my_lseek,
};
int test_init(void)
{
int ret;
ret = init_gpiodev(&my_dev);
if(0 != ret)
{
printk("init_gpiodev.\n");
goto err0;
}
my_dev.no = MKDEV(52,0);
ret = register_chrdev_region(my_dev.no, 1, "mygpio_dev");
if(0 != ret)
{
printk("register_chrdev_region.\n");
goto err1;
}
cdev_init(&my_dev.dev, &my_ops);
cdev_add(&my_dev.dev, my_dev.no, 1);
return 0;
err1:
destory_gpiodev(&my_dev);
err0:
return ret;
}
void test_exit(void)
{
cdev_del(&my_dev.dev);
unregister_chrdev_region(my_dev.no, 1);
destory_gpiodev(&my_dev);
}
module_init(test_init);
module_exit(test_exit);
ioctl函数用到的命令,单独定义在.h文件中:mygpio.h
#ifndef __MYGPIO_H__
#define __MYGPIO_h__
#define LED_TYPE 'Z'
#define LED_ON _IOW(LED_TYPE, 0x0, int)
#define LED_OFF _IOW(LED_TYPE, 0x1, int)
#endif /*__MYGPIO_H__*/
Makefile
KERNELDIR = /lib/modules/$(shell uname -r)/build
default:
make -C $(KERNELDIR) M=$(shell pwd) modules
clean:
make -C $(KERNELDIR) M=$(shell pwd) modules clean
obj-m += mygpio.o
编译生成mygpio.ko文件
验证代码
app.c
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <fcntl.h>
#include "mygpio.h"
int parse_command(const char *cmd)
{
if (!strncasecmp(cmd, "on", 2)) {
return LED_ON;
} else if (!strncasecmp(cmd, "off", 3)) {
return LED_OFF;
} else {
return 0x55;
}
}
int main(int argc, char **argv)
{
int fd;
if (argc < 3) {
printf("Usage: %s <file> <on | off>\n", argv[0]);
return 0;
}
fd = open(argv[1], O_RDWR);
if (ioctl(fd, parse_command(argv[2]))) {
perror("ioctl failed");
}
close(fd);
}
创建设备文件: mknod led_node c 52 0
编译生成app,执行./app led_node on
./app led_node off
查看对应led灯点亮/灭