- #include <linux/module.h>
- #include <linux/kernel.h>
- #include <linux/init.h>
- #include <linux/fs.h>
- #include <linux/errno.h>
- #include <linux/cdev.h>
- #include <linux/types.h>
- #include <linux/device.h>
- #include <linux/mm.h>
- #include <linux/slab.h>
- #include <linux/sched.h>
- #include <linux/poll.h>
- #include <asm/uaccess.h>
- #include <asm/irq.h>
-
#include <asm/io.h>
-
#include <mach/map.h>
-
#include <mach/regs-clock.h>
-
#include <mach/regs-gpio.h>
-
#include <mach/gpio-bank-k.h>
-
#include <plat/gpio-cfg.h>
- #define DRIVER_DEBUG
- #define DEVICE_NAME "mydriver"
-
//#define DEVICE_MAJOR 252
- /* parameters */
- static int major;
- static int minor = 0;
-
static int nr_devs = 1; /* number of devices */
- struct class *my_class;
- struct mydriver_dev {
- struct cdev cdev; /* char device structure */
-
}*mydriver_devp; /* device structure pointer */
- /*
- * The open function of device driver.
- */
- static int mydriver_open(struct inode *inode, struct file *file)
-
{
- unsigned int led_tmp;
-
- #ifdef DRIVER_DEBUG
- printk("\nIn the open function of driver!");
-
#endif
-
-
//turn off all lights
-
led_tmp = readl(S3C64XX_GPKDAT);
-
led_tmp = (led_tmp | 0x000000f0);
- writel(led_tmp, S3C64XX_GPKDAT);
- return 0;
-
}
- static int mydriver_release(struct inode *inode, struct file *filp)
- {
- unsigned int led_tmp;
- #ifdef DRIVER_DEBUG
- printk("In the release function of driver!\n");
- #endif
-
-
//turn off all lights
-
led_tmp = readl(S3C64XX_GPKDAT);
-
led_tmp = (led_tmp | 0x000000f0);
- writel(led_tmp, S3C64XX_GPKDAT);
- return 0;
-
}
- /*
- * The read function of device driver.
- */
- static int mydriver_read(struct file *filp, char __user *buff, size_t count, loff_t *offp)
-
{
-
char led_state_read;
- #ifdef DRIVER_DEBUG
- printk("\nIn the read function of driver!");
-
#endif
-
- led_state_read = readl(S3C64XX_GPKDAT);
-
copy_to_user(buff, &led_state_read, 1);
- return 1;
-
}
- /*
- * The write function of device driver.
- */
- static ssize_t mydriver_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos)
- {
-
unsigned int led_tmp;
-
char led_state_write;
- #ifdef DRIVER_DEBUG
- printk("\nIn the write function of driver!");
- #endif
-
copy_from_user(&led_state_write, buf, 1);
- if (led_state_write <= 0x0f) {
-
led_tmp = readl(S3C64XX_GPKDAT);
-
led_tmp &= (led_state_write << 4); //GPK[7:4] is connected to LEDs
-
led_tmp |= (led_state_write << 4);
-
writel(led_tmp, S3C64XX_GPKDAT);
-
} else {
-
printk("ERR: Illegal Input!\n");
-
printk("Please input the data that not over 16.\n");
-
}
- return 1;
-
}
- /*
- * The file operations for the device
- */
- static struct file_operations mydriver_fops = {
- .owner = THIS_MODULE,
- .open = mydriver_open,
- .release = mydriver_release,
- .read = mydriver_read,
- .write = mydriver_write
-
};
- /*
- * Set up a cdev entry.
- */
- static void mydriver_setup_cdev(struct mydriver_dev *dev, int index)
- {
-
int err, devno;
-
devno= MKDEV(major, minor + index);
-
- cdev_init(&dev->cdev, &mydriver_fops);
- dev->cdev.owner = THIS_MODULE;
- err = cdev_add (&dev->cdev, devno, 1);
- if (err)
- printk(KERN_NOTICE "Error %d adding mydriver%d", err, index);
-
}
- /*
- * Initialize the pipe devs; return how many we did.
- */
- static int __init mydriver_init_module(void)
- {
- int ret;
- dev_t devno = MKDEV(major, minor);
/* 注册设备 */
- if (major) /* 静态申请主设备号 */
- ret = register_chrdev_region(devno, nr_devs, DEVICE_NAME);
- else { /* 动态分配主设备号 */
- ret = alloc_chrdev_region(&devno, minor, nr_devs, DEVICE_NAME);
- major = MAJOR(devno);
- }
- if (ret < 0) {
- printk(KERN_WARNING "%s: can't get major %d\n", DEVICE_NAME, major);
- return ret;
-
}
/* 动态申请设备结构体的内存并清零 */
- mydriver_devp = kzalloc(sizeof(struct mydriver_dev), GFP_KERNEL);
- if (!mydriver_devp) {
- ret = - ENOMEM;
-
goto fail_malloc;
-
- }
/* 初始化字符设备结构体 */
-
/* The following step must after kmalloc() and memset() */
- mydriver_setup_cdev(mydriver_devp, 0);
/* 自动创建设备文件 */
-
/* 1. create your own class under /sys
-
* 2. register your own device in sys
-
* this will cause udev to create corresponding device node
- */
- my_class = class_create(THIS_MODULE, "my_class");
- if (IS_ERR(my_class)) {
- printk(DEVICE_NAME " failed in creating class./n");
- return -1;
- }
-
device_create(my_class, NULL, MKDEV(major, minor), NULL, "mydriver""%d", 0);
- printk(DEVICE_NAME "\tinitialized, major = %d, minor = %d.\n", major, minor);
- return 0;
-
fail_malloc:
-
unregister_chrdev_region(devno, 1);
-
- return ret;
-
}
- /*
- * This is called by cleanup_module or on failure.
- */
- static void __exit mydriver_exit_module(void)
- {
- device_destroy(my_class, MKDEV(major, minor));
-
class_destroy(my_class);
-
cdev_del(&mydriver_devp->cdev);
- kfree(mydriver_devp);
- unregister_chrdev_region(MKDEV(major, minor), 1);
- printk(DEVICE_NAME "\texited!\n");
-
}
- module_init(mydriver_init_module);
-
module_exit(mydriver_exit_module);
- MODULE_AUTHOR("Jason Lu");
- MODULE_VERSION("0.1.0");
- MODULE_DESCRIPTION("Jason's blog: http://blog.chinaunix.net/space.php?uid=20746260");
- MODULE_LICENSE("Dual MPL/GPL");
2.应用程序
- #include
- #include
- #include
- #include
- #include
- #include
- int main()
- {
- int fd, i = 0;
- char led_read_buf = 0, led_write_buf = 0;
- printf("--------------------------------------------------------------\n");
- printf("--- This is the begining of the test program of my driver! ---\n");
- printf("--------------------------------------------------------------\n");
- sleep(1); //加入延时是为了打印信息和程序执行过程同步输出,下同
- /* 1. open the device driver */
- printf("1. open device driver:\n");
- fd = open("/dev/mydriver0", O_RDWR);
- if (fd < 0) {
- printf("Device open error!\n");
- exit(1);
- }
- sleep(1);
- /* 2. Read LED status */
- printf("2. Read LED status:\n");
- if (read(fd, &led_read_buf, 1) < 0) {
- printf("Read data error!\n");
- exit(1);
- }
- /* GPK[7:4] is connected to LEDs */
- printf("The status of GPK[7:0] is 0x%x.\n", led_read_buf);
- sleep(1);
- /* 3. Control LED lights */
- printf("3. Control LED lights:\n");
- while (i < 128) {
- led_write_buf = i % 16;
- write(fd, &led_write_buf, 1);
- sleep(1);
- i++;
- }
- /* 4. close device driver */
- close(fd);
- printf("---------------------------------------------------------\n");
- printf("--- This is the end of the test program of my driver! ---\n");
- printf("---------------------------------------------------------\n");
- return 0;
- }
3. 把驱动程序加入内核(One)
a.把上述驱动程序添加到/driver/char目录下;
b.把/driver/char目录下的Kconfig文件里,添加如下几行:
- config MY_DRIVER
- tristate "My driver for FriendlyARM Tiny6410 development boards"
- depends on MACH_MINI6410
- default m if MACH_MINI6410 //默认是内核模块
- help
- this is My driver for FriendlyARM Tiny6410 development boards
c.把/driver/char目录下的Makefile文件里,添加如下一行:
- obj-$(CONFIG_MY_DRIVER) += my_driver.o
d.cp config_mini6410_n43 .config
e.make menuconfig
f.在出现的配置界面里,输入D,找到Device Drivers,按Enter键进入子界面
g.在子配置界面里,两次输入C,找到Character devices,按Enter键进入子界面
h.查看是否有一行是 My driver for FriendlyARM Tiny6410 development boards
i.退出,make uImage
j.待生成uImage后,在内核工程主目录下输入:
- mkimage -A arm -O linux -T kernel -C none -a 0x50018000 -e 0x50018040 -n "Jason Linux" -d arch/arm/boot/zImage ./uImage
k.把内核通过SD卡烧入开发板
l.make modules 驱动程序如果没有错误的话,就会生成my_driver.ko
m.通过串口终端rz命令,传入my_driver.ko以及应用程序的可执行文件drivertest,把前者放到lib/modules/2.6.38-FriendlyARM/kernel/drivers/char/目录下
n.然后如下图操作:
4. 把驱动程序加入内核(Two)
注:modules:下一行必须以TAB键开始,下同,不解释。
- DRIVER_NAME =my_led
- KERNELDIR =/home/source/friendlyarm/linux-2.6.38-jason/
- PWD :=$(shell pwd)
- INSTALLDIR =$(KERNELDIR)/drivers/char/
- CROSS_COMPILE =/home/source/friendlyarm/toolschain/4.5.1/bin/arm-linux-
- CC =$(CROSS_COMPILE)gcc
- obj-m :=$(DRIVER_NAME).o
- modules:
- $(MAKE) -C $(KERNELDIR) M=$(PWD) modules
- modules_install:
- cp $(DRIVER_NAME).ko $(INSTALLDIR)
- clean:
- rm -rf *.o *~ core .depend .*.cmd *.mod.c .tmp_versions *.symvers *.order
- clean_all:
- rm -rf *.ko *.o *~ core .depend .*.cmd *.mod.c .tmp_versions *.symvers *.order
- .PHONY: modules modules_install clean