Now, let's write our own first character device driver--light LEDs. (Imperfect, back to perfect)
Hardware platform: Exynos4412 (FS4412)
Writing the drive is divided into the following steps:
A--View the schematic, data manual, understand the equipment operation method;
B-Find a similar driver in the kernel, develop it as a template, and sometimes need to start from scratch;
C-Implement driver initialization: For example, register the driver with the kernel so that the application can pass in the filename and the kernel will find the appropriate driver;
D--The design of the operation to be implemented, such as open, close, read, write and other functions;
e-To implement interrupt services (interrupts are not required for each device driver);
F--Compile the driver into the kernel or load it with the INSMOD command;
g--test driver;
Here is a light LED driver:
The first step, of course, is to view the manual, view the schematic diagram, find the corresponding register;
To view the manual, the four led registers are:
Led2
Gpx2con 0X11000C40
Gpx2dat 0X11000C44
led3
Gpx1con 0X11000C20
Gpx1dat 0X11000C24
led4 3-4 3-5
Gpf3con 0x114001e0
Gpf3dat 0x114001e4
Here to note : ARM architecture is IO memory, must be mapped ioremap (); Its role is the mapping of physical memory to virtual memory . Using the Writel readl These two functions, the detailed explanation will not be on the back, look at the simple usage first:
Take LED2 as an example, the following is address mapping and reading and writing:
int *pgpx2con ;
int *pgpx2dat;
Pgpx2con = Ioremap (Gpx2con, 4);
Pgpx2dat = Ioremap (gpx2dat,4);
Readl (Pgpx2con);
Writel (0x01, Pgpx2dat);
Here is the driver, which will be more complete later
#include <linux/module.h> #include <linux/fs.h> #include <linux/cdev.h> #include <linux/device.h
> #include <asm/io.h> #include <asm/uaccess.h> static int major = 250;
static int minor=0;
Static dev_t Devno;
static struct class *cls;
static struct device *test_device; #define GPX2CON 0x11000c40 #define GPX2DAT 0x11000c44 #define GPX1CON 0x11000c20 #define GPX1DAT 0x11000c2
4 #define GPF3CON 0x114001e0 #define GPF3DAT 0x114001e4 static int *pgpx2con;
static int *pgpx2dat;
static int *pgpx1con;
static int *pgpx1dat;
static int *pgpf3con;
static int *pgpf3dat;
void fs4412_led_off (int num); void fs4412_led_on (int num) {switch (num) {case 1:writel (READL (Pgpx2dat) | (
0x1<<7), Pgpx2dat);
Break Case 2:writel (Readl (Pgpx1dat) | (
0x1<<0), Pgpx1dat);
Break Case 3:writel (Readl (Pgpf3dat) | (
0x1<<4), Pgpf3dat);
Break Case 4:writel (Readl (Pgpf3dat) | ( 0X1<<5), Pgpf3daT);
Break
Default:fs4412_led_off (1);
Fs4412_led_off (2);
Fs4412_led_off (3);
Fs4412_led_off (4);
Break
} void Fs4412_led_off (int num) {switch (num) {case 1:writel (Readl (Pgpx2dat) & (~ (0x1<<7)), Pgpx2dat);
Break
Case 2:writel (Readl (Pgpx1dat) & (~ (0x1<<0)), Pgpx1dat);
Break
Case 3:writel (Readl (Pgpf3dat) & (~ (0x1<<4)), Pgpf3dat);
Break
Case 4:writel (Readl (Pgpf3dat) & (~ (0x1<<5)), Pgpf3dat);
Break
} static int Led_open (struct inode *inode, struct file *filep) {//open fs4412_led_off (1);
Fs4412_led_off (2);
Fs4412_led_off (3);
Fs4412_led_off (4);
return 0;
static int led_release (struct inode *inode, struct file *filep) {//close fs4412_led_off (1);
Fs4412_led_off (2);
Fs4412_led_off (3);
Fs4412_led_off (4);
return 0;
Static ssize_t led_read (struct file *filep, char __user *buf, size_t len, loff_t *pos) {return 0;} Static ssize_t led_write (struct file *Filep, const char __user *buf, size_t len, loff_t *pos) {int led_num;
if (len!=4) {return-einval;
} if (Copy_from_user (&led_num,buf,len)) {return-efault;
} fs4412_led_on (Led_num);
PRINTK ("Led_num =%d \ n", led_num);
return 0; static struct file_operations hello_ops= {. open = Led_open,. Release = Led_release,. Read = Led_read,. WRI
Te = Led_write,};
static void Fs4412_led_init (void) {Pgpx2con = Ioremap (gpx2con,4);
Pgpx2dat = Ioremap (gpx2dat,4);
Pgpx1con = Ioremap (gpx1con,4);
Pgpx1dat =ioremap (gpx1dat,4);
Pgpf3con = Ioremap (gpf3con,4);
Pgpf3dat =ioremap (gpf3dat,4); Writel (Readl (Pgpx2con) & ~ (0xf<<28) | (
0x1<<28), Pgpx2con); Writel (Readl (Pgpx1con) & ~ (0xf<<0) | (
0x1<<0), Pgpx1con); Writel (Readl (Pgpf3con) & ~ (0xff<<16) | (
0X11<<16), Pgpf3con);
static int led_init (void) {int ret;
Devno = Mkdev (Major,minor);
ret = Register_chrdev (Major, "led", &hello_ops); CLS = Class_creatE (This_module, "MyClass");
if (Is_err (CLS)) {Unregister_chrdev (Major, "led");
Return-ebusy; } Test_device = Device_create (Cls,null,devno,null, "led");//mknod/dev/hello if (Is_err (Test_device)) {Class_destroy (c
LS);
Unregister_chrdev (Major, "led");
Return-ebusy;
} fs4412_led_init ();
return 0;
} void Fs4412_led_unmap (void) {Iounmap (Pgpx2con);
Iounmap (Pgpx2dat);
Iounmap (Pgpx1con);
Iounmap (Pgpx1dat);
Iounmap (Pgpf3con);
Iounmap (Pgpf3dat);
} static void Led_exit (void) {Fs4412_led_unmap ();
Device_destroy (CLS,DEVNO);
Class_destroy (CLS);
Unregister_chrdev (Major, "led");
PRINTK ("Led_exit \ n");
} module_license ("GPL");
Module_init (Led_init);
Module_exit (Led_exit);
Test program:
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h >
Main ()
{
int fd,i,lednum;
FD = open ("/dev/led", O_RDWR);
if (fd<0)
{
perror ("Open fail \ n");
return;
}
for (i=0;i<100;i++)
{
lednum=0;
Write (fd,&lednum,sizeof (int));
Lednum = i%4+1;
Write (fd,&lednum,sizeof (int));
Sleep (1);
}
Close (FD);
}
Makefile
Ifneq ($ (kernelrelease),)
obj-m:=hello.o
$ (Info "2nd")
else
#KDIR: =/lib/modules/$ ( Shell uname-r)/build
kdir: =/home/xiaoming/linux-3.14-fs4412
pwd:=$ (Shell PWD) all
:
$ (info) 1st ")
Make-c $ (kdir) m=$ (PWD) modules arm-none-linux-gnueabi-gcc test.c
sudo cp Hello.ko a.out/rootfs/ test/clean
:
rm-f *.ko *.o *.symvers *.mod.c *.mod.o *.order
endif
After the compilation finishes, copy A.out and Hello.ko to the Development Board:
# Insmod Hello.ko
#mknod/dev/hello C 250 0
#./a.out
will see the effect of the happy lantern.
The rear will be perfect for that drive.