PC操作系统:ubuntu 11.10
使用的开发板:am335x_evm
开发板使用的操作系统:linux 3.2
测试用例:
#include #include #include #include #include #include #include #include #include //register_chrdev_region
#include #include #include MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("SM");
MODULE_VERSION("0.0.1");
//MODULE_DEVICE("global memory");
MODULE_DESCRIPTION("led ...");
MODULE_ALIAS("LED");
/*******************************************/
#define NAME "leds"
#define GPIO_TO_PIN(bank, gpio) (32 * (bank) + (gpio))
#defineLED_GPIO_11
#defineLED_PIN_124
static int major =0;//定义主设备号
staticstruct class *led_class;
/*******************************************/
void led_on(void)
{
gpio_set_value(GPIO_TO_PIN(LED_GPIO_1,LED_PIN_1), 1);
}
EXPORT_SYMBOL(led_on);
void led_off(void)
{
gpio_set_value(GPIO_TO_PIN(LED_GPIO_1,LED_PIN_1), 0);
}
EXPORT_SYMBOL(led_off);
void led_init(void)
{
int result;
/* Allocating GPIOs and setting direction */
gpio_free(GPIO_TO_PIN(LED_GPIO_1,LED_PIN_1));
result = gpio_request(GPIO_TO_PIN(LED_GPIO_1,LED_PIN_1), "Leds");//usr1
if (result != 0)
printk("gpio_request(%d_%d) failed!\n",LED_GPIO_1,LED_PIN_1);
result = gpio_direction_output(GPIO_TO_PIN(LED_GPIO_1,LED_PIN_1), 1);
if (result != 0)
printk("gpio_direction(%d_%d) failed!\n",LED_GPIO_1,LED_PIN_1);
}
struct light_dev
{
struct cdev cdev;
unsigned char value;
};
struct light_dev *light_devp;
// 打开和关闭函数
int light_open(struct inode *inode,struct file *filp)
{
struct light_dev *dev;
// 获得设备结构体指针
dev = container_of(inode->i_cdev,struct light_dev,cdev);
// 让设备结构体作为设备的私有信息
filp->private_data = dev;
return 0;
}
int light_release(struct inode *inode,struct file *filp)
{
return 0;
}
// ioctl
long light_ioctl(struct file *filp,unsigned int cmd,
unsigned long arg)
{
struct light_dev *dev = filp->private_data;
switch(cmd)
{
case 0:
dev->value = 0;
led_off();
break;
case 1:
dev->value = 1;
led_on();
break;
default:
return -ENOTTY;
// break;
}
return 0;
}
//set up the cdev structure for a device
staticvoidled_setup_cdev(struct cdev* dev,int minor,struct file_operations *fops)
{
int err, devno = MKDEV(major,minor);
cdev_init(dev,fops);
dev->owner= THIS_MODULE;
dev->ops= fops;
err= cdev_add(dev,devno,1);
if(err)
printk(KERN_NOTICE"Error %d adding led%d",err,minor);
}
struct file_operations light_fops =
{
.owner = THIS_MODULE,
.unlocked_ioctl = light_ioctl,
.open = light_open,
.release = light_release,
};
staticstruct cdev cdev_led;
// 模块加载函数
int light_init(void)
{
int ret;
dev_tdev= MKDEV(major,0);
led_init();
printk(KERN_ALERT "led modules is install\n");
// ret=register_chrdev(major,NAME,&light_fops);
if(major)
ret= register_chrdev_region(dev,1,NAME);
else
{
ret= alloc_chrdev_region(&dev,0,1,NAME);
major= MAJOR(dev);
}
if(ret<0)
{
printk("unable to register myled driver!\n");
return ret;
}
printk(KERN_DEBUG"led device number : %x\n",dev);
led_setup_cdev(&cdev_led,0,&light_fops);
led_class= class_create(THIS_MODULE,"led_class");
if(IS_ERR(led_class))
{
printk(KERN_INFO"create led class error\n");
return -1;
}
device_create(led_class,NULL,dev,NULL,"led" "%d",MINOR(dev));
return 0;
}
// 模块卸载函数
void light_cleanup(void)
{
// unregister_chrdev(major,NAME);
device_destroy(led_class,MKDEV(major,0));
class_destroy(led_class);
cdev_del(&cdev_led);unregister_chrdev_region(MKDEV(major,0),1);
printk("Goodbye,cruel world!\n");
}
module_init(light_init);
module_exit(light_cleanup);
详解:
@a1@动态分配设备号
/**
* 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, //alloc_chrdev_region(0,0,1,"leds")
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;
}
@b1@ 获得并注册设备号
/** Register a single major with a specified minor range.
*
* If major == 0 this functions will dynamically allocate a major and return
* its number.
*
* If major > 0 this function will attempt to reserve the passed range of
* minors and will return zero on success.
*
* Returns a -ve errno on failure.
*/
static struct char_device_struct *
__register_chrdev_region(unsigned int major, unsigned int baseminor,
int minorct, const char *name)
{
struct char_device_struct *cd, **cp;
int ret = 0;
int i;
//分配并清空内存空间用以保存这个设备的主设备号等信息
cd = kzalloc(sizeof(struct char_device_struct), GFP_KERNEL);
if (cd == NULL)
return ERR_PTR(-ENOMEM);
//锁住临界区
mutex_lock(&chrdevs_lock);
//动态分配主设备号
/* temporary */
if (major == 0) {
for (i = ARRAY_SIZE(chrdevs)-1; i > 0; i--) {
if (chrdevs[i] == NULL)
break;
}
//当前没有可用的主设备号,异常退出
if (i == 0) {
ret = -EBUSY;
goto out;
}
//成功获得主设备号
major = i;
ret = major;
}
//根据当前例子填充数据
cd->major = major; //动态分配的主设备号
cd->baseminor = baseminor;//当前为 0 (如果有多个设备,其表示第一个次设备号)
cd->minorct = minorct; //设备个数 1
strlcpy(cd->name, name, sizeof(cd->name)); //设备名为 "leds"
//主设备号是通过hash表来管理的
i = major_to_index(major); //当前动态获得的主设备号在所有设备中的索引值(即位于那条hash列中)
/*
#if 0
/* fs/char_dev.c */
#define CHRDEV_MAJOR_HASH_SIZE 255
/* index in the above */
static inline int major_to_index(unsigned major)
{
return major % CHRDEV_MAJOR_HASH_SIZE;
}
static struct char_device_struct {
struct char_device_struct *next;
unsigned int major;
unsigned int baseminor;
int minorct;
char name[64];
struct cdev *cdev; /* will die */
} *chrdevs[CHRDEV_MAJOR_HASH_SIZE];
#endif
*/
/* chrdevs[i]
@1@ 假设主设备号28并没有被使用,dev1的主设备号为28,
@2@
@3@
*/
for (cp = &chrdevs[i]; *cp; cp = &(*cp)->next)
if ((*cp)->major > major ||
((*cp)->major == major &&
(((*cp)->baseminor >= baseminor) ||
((*cp)->baseminor + (*cp)->minorct > baseminor))))
break;
/* Check for overlapping minor ranges. */
if (*cp && (*cp)->major == major) {
int old_min = (*cp)->baseminor;
int old_max = (*cp)->baseminor + (*cp)->minorct - 1;
int new_min = baseminor;
int new_max = baseminor + minorct - 1;
/* New driver overlaps from the left. */
if (new_max >= old_min && new_max <= old_max) {
ret = -EBUSY;
goto out;
}
/* New driver overlaps from the right. */
if (new_min <= old_max && new_min >= old_min) {
ret = -EBUSY;
goto out;
}
}
cd->next = *cp;
*cp = cd;
mutex_unlock(&chrdevs_lock);
return cd;
out:
mutex_unlock(&chrdevs_lock);
kfree(cd);
return ERR_PTR(ret);
}
hjhk
@b2@返回设备号
@b DONE@
@a2@初始化设备
@b1@
@b2@
@b3@
@b DONE@
@a3@添加设备
@b1@
@b2@
@b3@
@b DONE@
@a4@在sysfs中创建设备类型
@b1@
@b2@
@b3@
@b DONE@
@a5@在sysfs中对应的设备类型中添加设备
@b1@
@b2@
@b3@
@b DONE@
@a DONE@