一、字符驱动
以LED字符驱动为例:
#include <linux/module.h>
#include <linux/init.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/io.h>
#define LED_MAGIC 'L' /* 幻数 */
#define LED_ON _IO(LED_MAGIC, 0) /* 0表示序号 */
#define LED_OFF _IO(LED_MAGIC, 1) /* 1表示序号 */
#define GPC0CON 0xE0200060
#define GPC0DAT 0xE0200064
unsigned int *led_con;
unsigned int *led_data;
struct cdev cdev;
dev_t devno;
int led_open(struct inode *node, struct file *filp)
{
led_con = ioremap(GPC0CON, 4); //地址映射
writel(0x11000, led_con); //写数据
led_data = ioremap(GPC0DAT, 4);
return 0;
}
long led_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
switch(cmd)
{
case LED_ON:
writel(0x18, led_data);
return 0;
case LED_OFF:
writel(0x00, led_data);
return 0;
default:
return -EINVAL;
}
}
static struct file_operations led_fops =
{
.open = led_open, //设备方法1
.unlocked_ioctl = led_ioctl, //设备方法2
};
static int led_init()
{
/*初始化cdev结构*/
cdev_init(&cdev, &led_fops);
/* 动态分配设备号 */
alloc_chrdev_region(&devno, 0, 1, "myled");
/* 注册字符设备 */
cdev_add(&cdev, devno, 1);
return 0;
}
static void led_exit()
{
cdev_del(&cdev); /*注销设备*/
unregister_chrdev_region(devno, 1); /*释放设备号*/
}
MODULE_LICENSE("GPL");
module_init(led_init);
module_exit(led_exit);
对应的应用程序:
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#define LED_MAGIC 'L' /* 幻数 */
#define LED_ON _IO(LED_MAGIC, 0) /* 0表示序号 */
#define LED_OFF _IO(LED_MAGIC, 1) /* 1表示序号 */
int main(int argc, char *argv[])
{
int fd;
int cmd;
if(argc < 2)
{
printf("please enter the second para!\n");
return 0;
}
cmd = atoi(argv[1]); //将字符型数据转化为整数
fd = open("/dev/myled", O_RDWR);
if(cmd == 1)
ioctl(fd, LED_ON);
else if(cmd == 0)
ioctl(fd, LED_OFF);
}
二、平台驱动
以LED驱动为例:
平台device驱动:
/*************************************************************
* file : led_device.c
* type : device module
* description : the led device of platform driver.
* modify : NA
*************************************************************/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/platform_device.h>
struct led_info
{
unsigned int pin_bit;
};
static struct led_info ledpos = {
.pin_bit = 1<<3 | 1<<4,
};
static struct resource led_resource[] = {
[0] = {
.start = 0xE0200060,
.end = 0xE0200060 + 16 -1,
.flags = IORESOURCE_MEM,
},
};
/* Function : led_release()
* Description : release
*/
static void led_release(struct device *dev)
{
printk("led_release(): leds device remove.\n");
}
static struct platform_device led_device = {
.name = "s5pv210leds",
.id = -1,
.num_resources = ARRAY_SIZE(led_resource),
.resource = led_resource,
.dev = {
.release = led_release,
.platform_data = &ledpos,
},
};
/* Function : led_dev_init()
* Description : module init
*/
static int __init led_dev_init(void)
{
platform_device_register(&led_device);
return 0;
}
/* Function : led_dev_exit()
* Description : module exit
*/
static void __exit led_dev_exit(void)
{
platform_device_unregister(&led_device);
}
module_init(led_dev_init);
module_exit(led_dev_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("LKC");
MODULE_VERSION("V1.0");
MODULE_DESCRIPTION("This is led device of platform driver.");
平台driver驱动:
/*************************************************************
* file : led_driver.c
* type : driver module
* description : the led driver of platform driver.
* modify : NA
*************************************************************/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/ioctl.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/slab.h> // alloc() kfree()
#include <mach/map.h>
#include <asm/io.h>
#include <linux/platform_device.h>
#define DEVICE_NAME "s5pv210_leds"
#define LED_ON 1
#define LED_OFF 0
struct led_info
{
unsigned int pin_bit;
};
struct gpio_reg
{
volatile unsigned long vr_gpccon;
volatile unsigned long vr_gpcdat;
};
struct s5pv210_leds_drv
{
dev_t dev_nr;
struct cdev *cdev_led;
struct file_operations leds_ops;
struct class *led_class;
struct device *this_device;
struct led_info *led_pdata;
struct gpio_reg *vr_gpioreg;
struct resource *led_res;
};
static struct s5pv210_leds_drv s5pv210_leds_drv_data;
/* Function : leds_open()
* Description : open
*/
static int leds_open(struct inode *inode, struct file *file)
{
unsigned int con = 0, dat = 0;
unsigned int i = 0;
unsigned int pin_bit;
pin_bit = s5pv210_leds_drv_data.led_pdata->pin_bit;
con = s5pv210_leds_drv_data.vr_gpioreg->vr_gpccon;
dat = s5pv210_leds_drv_data.vr_gpioreg->vr_gpcdat;
printk("leds_open() is called.\n");
for(i=0;i<8;i++)
{
if((1 << i) & pin_bit)
{
con &= ~(0xf << i*4);
con |= (1 << i*4);
dat |= (1 << i); // close two leds
}
}
s5pv210_leds_drv_data.vr_gpioreg->vr_gpccon = con;
s5pv210_leds_drv_data.vr_gpioreg->vr_gpcdat = dat;
return 0;
}
/* Function : leds_ioctl()
* Description : ioctl
*/
static long leds_ioctl(struct file *flip, unsigned int cmd, unsigned long arg)
{
unsigned int dat = 0;
unsigned int pin_bit;
unsigned int i = 0, j = 0;
unsigned int led_bit[8];
pin_bit = s5pv210_leds_drv_data.led_pdata->pin_bit;
for(i=0;i<8;i++)
{
if((1 << i) & pin_bit)
{
led_bit[j] = i;
//printk("led_bit[%d] = %d.\n", j, i);
j++;
}
}
if(arg >= j)
return -EINVAL;
mb();
dat = s5pv210_leds_drv_data.vr_gpioreg->vr_gpcdat;
mb();
//printk("cmd:%d, dat:%d\n", cmd, dat);
switch(cmd)
{
case LED_ON:
dat |= (1 << led_bit[arg]);
s5pv210_leds_drv_data.vr_gpioreg->vr_gpcdat = dat;
return 0;
case LED_OFF:
dat &= ~(1 << led_bit[arg]);
s5pv210_leds_drv_data.vr_gpioreg->vr_gpcdat = dat;
return 0;
default:
printk("Err:iotctl() cmd is err.\n");
break;
}
return 0;
}
/* Function : leds_probe()
* Description : probe
*/
static int led_probe(struct platform_device *pdev)
{
int major;
int msize, err;
//printk("led_probe() is called.\n");
//(1) platform_data
s5pv210_leds_drv_data.led_pdata = pdev->dev.platform_data;
if( !(s5pv210_leds_drv_data.led_pdata) )
{
printk("Err:get platform_data error.\n");
return -1;
}
//(2) resource
s5pv210_leds_drv_data.led_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if( !(s5pv210_leds_drv_data.led_res) )
{
printk("Err:get resource error.\n");
return -1;
}
msize = s5pv210_leds_drv_data.led_res->end - s5pv210_leds_drv_data.led_res->start + 1;
s5pv210_leds_drv_data.led_res = request_mem_region(s5pv210_leds_drv_data.led_res->start, msize, DEVICE_NAME);
if( !(s5pv210_leds_drv_data.led_res) )
{
printk("Err:request resource error.\n");
return -1;
}
//(3) gpio_reg
s5pv210_leds_drv_data.vr_gpioreg = ioremap_nocache(s5pv210_leds_drv_data.led_res->start, msize);
if( !(s5pv210_leds_drv_data.vr_gpioreg) )
{
printk("Err:ioremap_nocache error.\n");
release_region(s5pv210_leds_drv_data.led_res->start, msize);
return -1;
}
memset(s5pv210_leds_drv_data.vr_gpioreg, 0, sizeof(struct gpio_reg));
// register device
major = alloc_chrdev_region(&s5pv210_leds_drv_data.dev_nr, 0, 1, DEVICE_NAME);
if(major)
{
printk("Err:request dev_nr error.\n");
release_region(s5pv210_leds_drv_data.led_res->start, msize);
iounmap(s5pv210_leds_drv_data.vr_gpioreg);
return -1;
}
s5pv210_leds_drv_data.cdev_led = cdev_alloc();
cdev_init(s5pv210_leds_drv_data.cdev_led, &s5pv210_leds_drv_data.leds_ops);
s5pv210_leds_drv_data.leds_ops.owner = THIS_MODULE;
s5pv210_leds_drv_data.leds_ops.open = leds_open;
s5pv210_leds_drv_data.leds_ops.unlocked_ioctl = leds_ioctl;
err = cdev_add(s5pv210_leds_drv_data.cdev_led, s5pv210_leds_drv_data.dev_nr, 1);
if(err)
{
printk("Err:cdev_add error.\n");
unregister_chrdev_region(s5pv210_leds_drv_data.dev_nr, 1);
cdev_del(s5pv210_leds_drv_data.cdev_led);
kfree(s5pv210_leds_drv_data.cdev_led);
release_region(s5pv210_leds_drv_data.led_res->start, msize);
iounmap(s5pv210_leds_drv_data.vr_gpioreg);
return -1;
}
// class create
s5pv210_leds_drv_data.led_class = class_create(THIS_MODULE, "tq210_leds");
if(IS_ERR(s5pv210_leds_drv_data.led_class))
{
printk("Err:failed in creating class.\n");
unregister_chrdev_region(s5pv210_leds_drv_data.dev_nr, 1);
cdev_del(s5pv210_leds_drv_data.cdev_led);
kfree(s5pv210_leds_drv_data.cdev_led);
release_region(s5pv210_leds_drv_data.led_res->start, msize);
iounmap(s5pv210_leds_drv_data.vr_gpioreg);
return PTR_ERR(s5pv210_leds_drv_data.led_class);
}
// device create
s5pv210_leds_drv_data.this_device = device_create(s5pv210_leds_drv_data.led_class, NULL,
s5pv210_leds_drv_data.dev_nr, NULL, DEVICE_NAME);
if(IS_ERR(s5pv210_leds_drv_data.this_device))
{
printk("Err:failed in creating device.\n");
unregister_chrdev_region(s5pv210_leds_drv_data.dev_nr, 1);
cdev_del(s5pv210_leds_drv_data.cdev_led);
kfree(s5pv210_leds_drv_data.cdev_led);
release_region(s5pv210_leds_drv_data.led_res->start, msize);
iounmap(s5pv210_leds_drv_data.vr_gpioreg);
return PTR_ERR(s5pv210_leds_drv_data.this_device);
}
printk("leds initilized.\n");
return 0;
}
/* Function : leds_remove()
* Description : remove
*/
static int led_remove(struct platform_device *pdev)
{
device_destroy(s5pv210_leds_drv_data.led_class, s5pv210_leds_drv_data.dev_nr);
class_destroy(s5pv210_leds_drv_data.led_class);
unregister_chrdev_region(s5pv210_leds_drv_data.dev_nr, 1);
cdev_del(s5pv210_leds_drv_data.cdev_led);
kfree(s5pv210_leds_drv_data.cdev_led);
release_region(s5pv210_leds_drv_data.led_res->start,
s5pv210_leds_drv_data.led_res->end - s5pv210_leds_drv_data.led_res->start + 1);
iounmap(s5pv210_leds_drv_data.vr_gpioreg);
printk("leds driver remove.\n");
return 0;
}
static struct platform_driver led_driver = {
.probe = led_probe,
.remove = led_remove,
.driver = {
.owner = THIS_MODULE,
.name = "s5pv210leds",
},
};
/* Function : led_drv_init()
* Description : module init
*/
static int __init led_drv_init(void)
{
platform_driver_register(&led_driver);
return 0;
}
/* Function : led_drv_exit()
* Description : module exit
*/
static void __exit led_drv_exit(void)
{
platform_driver_unregister(&led_driver);
}
module_init(led_drv_init);
module_exit(led_drv_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("LKC");
MODULE_VERSION("V1.0");
MODULE_DESCRIPTION("This is led driver of platform driver.");
对应的应用程序:
/*************************************************************
* file : app_dev_drv.c
* type : application module
* description : leds platform device driver application.
* modify : NA
*************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#define DEVICE_NAME "/dev/s5pv210_leds"
int main(int arg, char *argv[])
{
int fd = 0;
int i = 0;
int ret = 0;
fd = open(DEVICE_NAME, O_RDONLY);
if(fd < 0)
{
printf(DEVICE_NAME " can not open device.\n");
exit(0);
}
while(1)
{
for(i=0;i<2;i++)
{
ret = ioctl(fd, 0, i);
if(ret < 0)
{
perror("ioctl(fd, 0, 0)\n");
exit(i);
}
sleep(1);
}
for(i=0;i<2;i++)
{
ret = ioctl(fd, 1, i);
if(ret < 0)
{
perror("ioctl(fd, 1, 0)\n");
exit(i);
}
sleep(1);
}
}
close(fd);
return 0;
}