Linux驱动程序例程

一、字符驱动

以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;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值