【吃灰板子捡起来】新字符设备驱动开发实验

一、设备号

定义:/fs/char_dev.c

#include <linux/fs.h>

/**
 * register_chrdev_region() - register a range of device numbers
 * @from: the first in the desired range of device numbers; must include
 *        the major number.
 * @count: the number of consecutive device numbers required
 * @name: the name of the device or driver.
 *
 * Return value is zero on success, a negative error code on failure.
 */
int register_chrdev_region(dev_t from, unsigned count, const char *name)
{
	struct char_device_struct *cd;
	dev_t to = from + count;
	dev_t n, next;

	for (n = from; n < to; n = next) {
		next = MKDEV(MAJOR(n)+1, 0);
		if (next > to)
			next = to;
		cd = __register_chrdev_region(MAJOR(n), MINOR(n),
			       next - n, name);
		if (IS_ERR(cd))
			goto fail;
	}
	return 0;
fail:
	to = n;
	for (n = from; n < to; n = next) {
		next = MKDEV(MAJOR(n)+1, 0);
		kfree(__unregister_chrdev_region(MAJOR(n), MINOR(n), next - n));
	}
	return PTR_ERR(cd);
}

/**
 * alloc_chrdev_region() - register a range of char device numbers
 * @dev: output parameter for first assigned number
 * @baseminor: first of the requested range of minor numbers
 * @count: the number of minor numbers required
 * @name: the name of the associated device or driver
 *
 * Allocates a range of char device numbers.  The major number will be
 * chosen dynamically, and returned (along with the first minor number)
 * in @dev.  Returns zero or a negative error code.
 */
int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count,
			const char *name)
{
	struct char_device_struct *cd;
	cd = __register_chrdev_region(0, baseminor, count, name);
	if (IS_ERR(cd))
		return PTR_ERR(cd);
	*dev = MKDEV(cd->major, cd->baseminor);
	return 0;
}

/**
 * unregister_chrdev_region() - return a range of device numbers
 * @from: the first in the range of numbers to unregister
 * @count: the number of device numbers to unregister
 *
 * This function will unregister a range of @count device numbers,
 * starting with @from.  The caller should normally be the one who
 * allocated those numbers in the first place...
 */
void unregister_chrdev_region(dev_t from, unsigned count)
{
	dev_t to = from + count;
	dev_t n, next;

	for (n = from; n < to; n = next) {
		next = MKDEV(MAJOR(n)+1, 0);
		if (next > to)
			next = to;
		kfree(__unregister_chrdev_region(MAJOR(n), MINOR(n), next - n));
	}
}

二、字符设备

定义:/fs/char_dev.c

#include <linux/cdev.h>

/**
 * cdev_init() - initialize a cdev structure
 * @cdev: the structure to initialize
 * @fops: the file_operations for this device
 *
 * Initializes @cdev, remembering @fops, making it ready to add to the
 * system with cdev_add().
 */
void cdev_init(struct cdev *cdev, const struct file_operations *fops)
{
	memset(cdev, 0, sizeof *cdev);
	INIT_LIST_HEAD(&cdev->list);
	kobject_init(&cdev->kobj, &ktype_cdev_default);
	cdev->ops = fops;
}
/**
 * cdev_add() - add a char device to the system
 * @p: the cdev structure for the device
 * @dev: the first device number for which this device is responsible
 * @count: the number of consecutive minor numbers corresponding to this
 *         device
 *
 * cdev_add() adds the device represented by @p to the system, making it
 * live immediately.  A negative error code is returned on failure.
 */
int cdev_add(struct cdev *p, dev_t dev, unsigned count)
{
	int error;

	p->dev = dev;
	p->count = count;

	error = kobj_map(cdev_map, dev, count, NULL,
			 exact_match, exact_lock, p);
	if (error)
		return error;

	kobject_get(p->kobj.parent);

	return 0;
}
/**
 * cdev_del() - remove a cdev from the system
 * @p: the cdev structure to be removed
 *
 * cdev_del() removes @p from the system, possibly freeing the structure
 * itself.
 */
void cdev_del(struct cdev *p)
{
	cdev_unmap(p->dev, p->count);
	kobject_put(&p->kobj);
}

三、设备节点

class

#include <linux/device.h>

/* This is a #define to keep the compiler from merging different
 * instances of the __key variable */
#define class_create(owner, name)		\
({						\
	static struct lock_class_key __key;	\
	__class_create(owner, name, &__key);	\
})

extern void class_destroy(struct class *cls);

device:drivers/base/core.c

#include <linux/device.h>

/**
 * device_create - creates a device and registers it with sysfs
 * @class: pointer to the struct class that this device should be registered to
 * @parent: pointer to the parent struct device of this new device, if any
 * @devt: the dev_t for the char device to be added
 * @drvdata: the data to be added to the device for callbacks
 * @fmt: string for the device's name
 *
 * This function can be used by char device classes.  A struct device
 * will be created in sysfs, registered to the specified class.
 *
 * A "dev" file will be created, showing the dev_t for the device, if
 * the dev_t is not 0,0.
 * If a pointer to a parent struct device is passed in, the newly created
 * struct device will be a child of that device in sysfs.
 * The pointer to the struct device will be returned from the call.
 * Any further sysfs files that might be required can be created using this
 * pointer.
 *
 * Returns &struct device pointer on success, or ERR_PTR() on error.
 *
 * Note: the struct class passed to this function must have previously
 * been created with a call to class_create().
 */
struct device *device_create(struct class *class, struct device *parent,
			     dev_t devt, void *drvdata, const char *fmt, ...)
{
	va_list vargs;
	struct device *dev;

	va_start(vargs, fmt);
	dev = device_create_vargs(class, parent, devt, drvdata, fmt, vargs);
	va_end(vargs);
	return dev;
}

/**
 * device_destroy - removes a device that was created with device_create()
 * @class: pointer to the struct class that this device was registered with
 * @devt: the dev_t of the device that was previously registered
 *
 * This call unregisters and cleans up a device that was created with a
 * call to device_create().
 */
void device_destroy(struct class *class, dev_t devt)
{
	struct device *dev;

	dev = class_find_device(class, NULL, &devt, __match_devt);
	if (dev) {
		put_device(dev);
		device_unregister(dev);
	}
}

四、驱动源码

#include <linux/module.h>
#include <linux/kernel.h> 
#include <linux/types.h>
#include <linux/init.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>

#include <asm/mach/map.h> 
#include <asm/uaccess.h>
#include <asm/io.h>


#define LED_ON  1
#define LED_OFF 0

#define CLK_PCLKEN0_BASE  (0xB0000218)
#define GPIOB_DIR_BASE    (0XB8003040)
#define GPIOBDATAOUT_BASE (0xB8003044)

static void __iomem *CLK_PCLKEN0;
static void __iomem *GPIOB_DIR;
static void __iomem *GPIOBDATAOUT;

struct newchrled_dev{
  struct cdev cdev;
  struct class *class;
  struct device *device;

  dev_t devid;
  int major;
  int minor; 
};
struct newchrled_dev newchrled;

static int  newchrled_devs = 1;                  /* Number of Devices */
static char newchrled_name[] = "newchrled";  /* Name of device */ 
 
static void led_switch(char led1_sw, char led2_sw)
{
  u32 val = 0;

  val = readl(GPIOBDATAOUT);
  //LED1
  if(led1_sw == LED_ON)
  {
    val &= ~(1<<4);
  }else if(led1_sw == LED_OFF)
  {
    val |= (1<<4);
  }else
  {
    printk("LED1 Control cmd error!\r\n");
  } 

  //LED2
  if(led2_sw == LED_ON)
  {
    val &= ~(1<<5);
  }else if(led2_sw == LED_OFF)
  {
    val |= (1<<5);
  }else
  {
    printk("LED2 Control cmd error!\r\n");
  } 
  writel(val,GPIOBDATAOUT);

}
static int newchrled_open(struct inode *inode, struct file *filp){

  return 0;
}

static int newchrled_release(struct inode *inode, struct file *filp){

  return 0;
}

static ssize_t newchrled_write(struct file *filp, const char __user *buf, size_t count, loff_t *ppos){

  int retvalue = 0;
  char databuf[2];

  copy_from_user(databuf,buf,count);
  if(retvalue < 0)
  {
    printk("Control LED Failed!\r\n");
    return -1;
  }

  led_switch(databuf[0],databuf[1]);

  return 0;

}


static struct file_operations newchrled_fops = {
  .owner = THIS_MODULE,
  .open = newchrled_open,
  .write = newchrled_write,
  .release = newchrled_release,
};

static int __init newchrled_init(void)
{
  int retvalue = 0;
  int result;
  u32 val = 0;
  /* init led */
  CLK_PCLKEN0 = ioremap(CLK_PCLKEN0_BASE,4);
  GPIOB_DIR   = ioremap(GPIOB_DIR_BASE,4);
  GPIOBDATAOUT= ioremap(GPIOBDATAOUT_BASE,4);

  val  = readl(CLK_PCLKEN0);
  val |= (1<<3);
  writel(val, CLK_PCLKEN0);

  val  = readl(GPIOB_DIR);
  val |= ((1<<4)|(1<<5));  //PB4 and PB5
  writel(val, GPIOB_DIR);

  val = readl(GPIOBDATAOUT);
  val |= (1<<4)|(1<<5);
  writel(val,GPIOBDATAOUT);

  newchrled.devid = 0;
  /* Register our major, and accept a dynamic number. */
  if(newchrled.devid)
  {
    result = register_chrdev_region(newchrled.devid, newchrled_devs, newchrled_name);
  }else
  {
    result = alloc_chrdev_region(&newchrled.devid, 0,newchrled_devs, newchrled_name);
    newchrled.major = MAJOR(newchrled.devid);
    newchrled.minor = MINOR(newchrled.devid);
  }
  if(result < 0)
  {
    printk("register chrdev failed!\r\n");
    return -1;
  }
  printk("newchrled major = %d, minor = %d\r\n",newchrled.major, newchrled.minor);

  /* Register a character device. */
  cdev_init(&newchrled.cdev, &newchrled_fops);
  newchrled.cdev.owner = THIS_MODULE;
  newchrled.cdev.ops  = &newchrled_fops;
  result = cdev_add(&newchrled.cdev, newchrled.devid, newchrled_devs);
  if(result < 0)
  {
    printk("cdev_add failed!\r\n"); 
    goto fail_chrdev; 
  }
  
  /* Create a sysfs class. */
  newchrled.class = class_create(THIS_MODULE, newchrled_name);
  if(IS_ERR(newchrled.class))
  {
    printk("class_create failed!\r\n");
    result = PTR_ERR(newchrled.class);
    goto fail_cdev; 
  }
 
  /* Create a device. */
  newchrled.device = device_create(newchrled.class, NULL, newchrled.devid, 0, newchrled_name); 
  if(IS_ERR(newchrled.device))
  {
    printk("device_create failed!\r\n");
    result = PTR_ERR(newchrled.device);
    goto fail_class;
  }
  printk("led_init\r\n");

  return 0;

fail_class:
  class_destroy(newchrled.class);
fail_cdev:
  cdev_del(&newchrled.cdev);
fail_chrdev:
  unregister_chrdev_region(newchrled.devid,newchrled_devs);
return result;
}

static void __exit newchrled_exit(void)
{
  u32 val = 0; 
  /* close the led */
  val = readl(GPIOBDATAOUT);
  val |= (1<<4)|(1<<5); 
  writel(val, GPIOBDATAOUT);

  iounmap(CLK_PCLKEN0);
  iounmap(GPIOB_DIR);
  iounmap(GPIOBDATAOUT);
  
  device_destroy(newchrled.class,newchrled.devid);
  class_destroy(newchrled.class);
  cdev_del(&newchrled.cdev);
  unregister_chrdev_region(newchrled.devid,newchrled_devs);

  printk("led_exit\r\n");

}
module_init(newchrled_init);
module_exit(newchrled_exit);


MODULE_LICENSE("GPL");
MODULE_AUTHOR("mx");

五、总结

1.如果未定义设备号(为0),则使用alloc_chrdev_region函数申请设备号;否则,使用register_chrdev_region函数申请设备号;
2.判断报错goto语句执行顺序,与初始化顺序相反;如果出错返回,需要将之前申请成功的,都销毁注销;
3.出口函数注销资源,先后后前;

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值