北京app网站开发如何对网站进行推广
本文主要探讨s5pv210的led驱动相关知识,包括驱动主次设备注册和取消,udev(mdev)机制,静态和动态映射操作寄存器。
字符设备驱动注册
老接口(register_chrdev)
static inline int register_chrdev(unsigned int major, const char *name,const struct file_operations *fops)
{return __register_chrdev(major, 0, 256, name, fops);
}
新接口(register_chrdev_region/alloc_chrdev_region)
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);
}
指定主次设备号(/proc/devices查看未使用驱动号)
form:起始设备号(static dev_t dev;)
count:次设备号数量
name:驱动名字
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;
}
内核分配主次设备号(从254开始查找未使用驱动号)
dev:内核分配的主次设备号(static dev_t dev;)
baseminor:次设备号起始号
count:次设备号个数
name:驱动名字
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;cd->minorct = minorct;strlcpy(cd->name, name, sizeof(cd->name));i = major_to_index(major);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);
}
注册都是用该函数,判断major为0则自动分配设备号,且从254依次递减查找未使用的号码
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));}
}
注销驱动
form:驱动号
count:驱动个数
cdev结构体(字符设备描述,cdev.h)
struct cdev {struct kobject kobj; /* 内嵌的 kobject 对象 */struct module *owner; /*所属模块*/ const struct file_operations *ops; /*文件操作结构体*/struct list_head list; /*内核链表*/dev_t dev;/*设备号*/unsigned int count;/*设备个数*/
};
主设备号MAJOR(dev_t dev)
次设备号MINOR(dev_t dev)
生成设备号MKDEV(int major, int minor)
cdev中dev_t dev定义设备号(高12位为主设备号,低20 位为次设备号)
MAJOR和MINOR根据已实例化的cdev中的dev_t dev获得主次设备号
MKDEV实例化cdev中的dev_t dev设备号
cdev结构体操作函数
struct cdev *cdev_alloc(void)
{struct cdev *p = kzalloc(sizeof(struct cdev), GFP_KERNEL);if (p) {INIT_LIST_HEAD(&p->list);kobject_init(&p->kobj, &ktype_cdev_dynamic);}return p;
}
动态申请cdev内存
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中的file_operation
int cdev_add(struct cdev *p, dev_t dev, unsigned count)
注册设备,通常发生在驱动模块的加载函数中
void cdev_put(struct cdev *p);
释放动态申请cdev内存
void cdev_del(struct cdev *p);
注销设备
驱动注册和取消流程:register_chrdev_region/alloc_chrdev_region + cdev_alloc + cdev_init + cdev_add + cdev_put + cdev_del + unregister_chrdev_region
udev(mdev)
udev是应用程序,驱动注册和注销信息传给udev,udev在应用层进行设备文件创建和删除
自动创建/sys/class/设备类/设备目录
/etc/init.d/rcS有关于udev的配置
echo /sbin/mdev > /proc/sys/kernel/hotplug
mdev -s
mdev -s:系统启动时扫描/sys/class和/sys/block查找dev文件
ex:/sys/class/tty/tty0/dev内容为4:0,即主设备号是4,次设备号是0,dev上级目录为设备名(tty0),/sys/class/下每个文件夹代表一个子系统
echo /sbin/mdev > /proc/sys/kernel/hotplug:有热插拔时内核调用/proc/sys/kernel/hotplug文件里指定应用程序来处理
创建驱动类
#define class_create(owner, name) \
({ \static struct lock_class_key __key; \__class_create(owner, name, &__key); \
})struct class *__class_create(struct module *owner, const char *name,struct lock_class_key *key)
{struct class *cls;int retval;cls = kzalloc(sizeof(*cls), GFP_KERNEL);if (!cls) {retval = -ENOMEM;goto error;}cls->name = name;cls->owner = owner;cls->class_release = class_create_release;retval = __class_register(cls, key);if (retval)goto error;return cls;error:kfree(cls);return ERR_PTR(retval);
}
owner:THIS_MODULE(通常)
name:类名
返回值class类指针
驱动类注销
void class_destroy(struct class *cls)
{if ((cls == NULL) || (IS_ERR(cls)))return;class_unregister(cls);
}
cls:创建的驱动类
创建设备
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;
}
class:类设备
parent:父设备,一般为NULL
devt:设备号
drvdata:设备数据
fmt:设备名字
注销设备
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);}
}
class:类设备
devt:设备号
udev机制流程:kernel(class_create,class_destroy,device_create,device_destroy)-->udev(mdev,应用层)-->/sys/class/设备类/设备
函数嵌套:class_create -->__class_create-->__class_register-->kset_register-->kobject_uevent
函数嵌套:device_create-->device_create_vargs-->kobject_set_name_vargs-->device_register-->device_add-->kobject_add-->kobject_uevent
静态映射表建立
/* minimal IO mapping */static struct map_desc s5p_iodesc[] __initdata = {{.virtual = (unsigned long)S5P_VA_CHIPID,.pfn = __phys_to_pfn(S5P_PA_CHIPID),.length = SZ_4K,.type = MT_DEVICE,}, {.virtual = (unsigned long)S3C_VA_SYS,.pfn = __phys_to_pfn(S5P_PA_SYSCON),.length = SZ_64K,.type = MT_DEVICE,}, {.virtual = (unsigned long)S3C_VA_UART,.pfn = __phys_to_pfn(S3C_PA_UART),.length = SZ_4K,.type = MT_DEVICE,}, {.virtual = (unsigned long)VA_VIC0,.pfn = __phys_to_pfn(S5P_PA_VIC0),.length = SZ_16K,.type = MT_DEVICE,}, {.virtual = (unsigned long)VA_VIC1,.pfn = __phys_to_pfn(S5P_PA_VIC1),.length = SZ_16K,.type = MT_DEVICE,}, {.virtual = (unsigned long)S3C_VA_TIMER,.pfn = __phys_to_pfn(S5P_PA_TIMER),.length = SZ_16K,.type = MT_DEVICE,}, {.virtual = (unsigned long)S5P_VA_GPIO,.pfn = __phys_to_pfn(S5P_PA_GPIO),.length = SZ_4K,.type = MT_DEVICE,},
};
建立映射表的三个关键:物理地址和虚拟地址相关宏定义,映射表建立函数
映射表建立函数:smdkc110_map_io(mach-smdkc110.c)-->s5p_init_io-->iotable_init
s5p_iodesc(cpu.c)是结构体数组数组中每个元素为一段段物理地址到虚拟地址映射,iotable_init函数将结构体数组格式表建立成MMU页表映射关系
开机-->start_kernel-->setup_arch-->paging_init-->devicemaps_init:mdesc->map_io();
内核寄存器读写接口
include/asm/io.h
#define writeb(v,c) ({ __iowmb(); writeb_relaxed(v,c); })
#define writew(v,c) ({ __iowmb(); writew_relaxed(v,c); })
#define writel(v,c) ({ __iowmb(); writel_relaxed(v,c); })
#define writesb(p,d,l) __raw_writesb(__mem_pci(p),d,l)
#define writesw(p,d,l) __raw_writesw(__mem_pci(p),d,l)
#define writesl(p,d,l) __raw_writesl(__mem_pci(p),d,l)#define readb(c) ({ u8 __v = readb_relaxed(c); __iormb(); __v; })
#define readw(c) ({ u16 __v = readw_relaxed(c); __iormb(); __v; })
#define readl(c) ({ u32 __v = readl_relaxed(c); __iormb(); __v; })
#define readsb(p,d,l) __raw_readsb(__mem_pci(p),d,l)
#define readsw(p,d,l) __raw_readsw(__mem_pci(p),d,l)
#define readsl(p,d,l) __raw_readsl(__mem_pci(p),d,l)v为值,c为地址,b为位,w为字节,l为4字节操
demo:
动态(静态)注册驱动
mdev机制
动态映射结构体方式操作寄存器
内核寄存器读写接口
led.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>#define FILE "/dev/led"char buf[16];
char led_cmd[3][4];void div_cmd(char *str)
{unsigned int m = 0;unsigned int n = 0;memset(led_cmd,'\0',sizeof(led_cmd));while(*str != '\0'){if(*str != ' '){led_cmd[m][n] = *str;n++;}else{led_cmd[m][n] = '\0';n = 0;m++;}str++;}
}void write_buf()
{int i;memset(buf,'\0',sizeof(buf));if(!strcmp(led_cmd[1],"on"))buf[0] = '0';if(!strcmp(led_cmd[1],"off"))buf[0] = '1';buf[1] = *(led_cmd[2]);
} int main()
{int fd = -1;char cmd[16];fd = open(FILE, O_RDWR);if (fd < 0){printf("open %s error.\n", FILE);return -1;}printf("input led cmd :led on|off 1|2|3\n");while(1){memset(cmd,'\0',sizeof(cmd));printf(">>>>");fgets(cmd,16,stdin);if(cmd[0] == '.' && cmd[1] == 'q'){break;}div_cmd(cmd);write_buf();write(fd,buf,strlen(buf));}close(fd);return 0;
}
led_dynamic_module.c
#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <asm/uaccess.h>
#include <mach/regs-gpio.h>
#include <mach/gpio-bank.h>
#include <linux/string.h>
#include <linux/io.h>
#include <linux/ioport.h>
#include <linux/cdev.h>
#include <linux/device.h>dev_t led_dev;
static struct cdev *led_cdev;
static struct class *led_class;
static struct device *led_device;//#define MAJOR 227 /*定义主设备号*/
#define BAEMINOR 0
#define DEV_COUNT 1
#define NAME "led"#define GPJ0_BASE_PA 0xe0200240/*
*动态映射结构体方式操作寄存器
typedef struct GPJ0REG
{volatile unsigned int gpj0con;volatile unsigned int gpj0dat;
} gpj0_reg_t;gpj0_reg_t *pGPJ0_BASE;
*///内核io接口读写寄存器
unsigned int led_tmp;
#define S5P_GPJ0REG(x) (x)
#define S5P_GPJ0CON S5P_GPJ0REG(0)
#define S5P_GPJ0DAT S5P_GPJ0REG(4)
static void __iomem *baseaddr;char kbuf[16];static ssize_t led_open(struct inode *inode, struct file *file)
{printk(KERN_INFO,"open led module\n");return 0;
}ssize_t led_read(struct file *file, char __user *buf, size_t size, loff_t *ppos)
{int ret = -1;copy_to_user(buf,kbuf,size);if(ret){printk(KERN_ERR,"copy to user error\n");return ret;}printk(KERN_INFO,"copy to user success\n");return 0;
}static ssize_t led_write(struct file *file, const char __user *ubuf,size_t count, loff_t *ppos)
{int ret = -1;int led;memset(kbuf, 0, sizeof(kbuf));ret = copy_from_user(kbuf,ubuf,count);if(ret){printk(KERN_ERR,"copy from user fail\n");return ret;}led = kbuf[1] - '0' + 2;if (kbuf[0] == '0'){//pGPJ0_BASE->gpj0con = 0x1111111;//pGPJ0_BASE->gpj0dat &= ~(1<<led);led_tmp &= ~(1<<led);writel(led_tmp,baseaddr + S5P_GPJ0DAT);}else if (kbuf[0] == '1'){//pGPJ0_BASE->gpj0con = 0x1111111;//pGPJ0_BASE->gpj0dat |= (1<<led);led_tmp |= (1<<led);writel(led_tmp,baseaddr + S5P_GPJ0DAT);}printk(KERN_INFO,"copy from user success\n");return 0;
}static int led_close(struct inode *inode, struct file *file)
{//pGPJ0_BASE->gpj0dat = (1<<3)|(1<<4)|(1<<5);led_tmp = (1<<3)|(1<<4)|(1<<5);writel(led_tmp,baseaddr + S5P_GPJ0DAT);printk(KERN_INFO,"close led module\n");return 0;
}static const struct file_operations led_fops = {.owner = THIS_MODULE,.open = led_open,.read = led_read,.write = led_write,.release = led_close
};static int __init led_char_dev_init()
{int ret = -1;//MKDEV(MAJOR, 0); /*定义主次设备号(dev_t led_dev)*///ret = register_chrdev_region(led_dev,DEV_COUNT,NAME); /*指定设备号注册方式*/ret = alloc_chrdev_region(&led_dev,BAEMINOR,DEV_COUNT,NAME);if(ret < 0){printk(KERN_ERR,"register led device fail\n");goto flag_1;}led_cdev = cdev_alloc();if(led_cdev == NULL){printk(KERN_ERR,"apply cdev memory fail\n");goto flag_2;}cdev_init(led_cdev,&led_fops);//类同cdev_init//led_cdev->owner = THIS_MODULE;//led_cdev->ops = &led_fops;ret = cdev_add(led_cdev,led_dev,DEV_COUNT);if(ret){printk(KERN_ERR,"device num bind fail\n");goto flag_3;}printk(KERN_INFO,"device num bind sucess\n");led_class = class_create(THIS_MODULE,"led_class");if(IS_ERR(led_class)){printk(KERN_ERR,"led class create fail\n");goto flag_4;}printk(KERN_INFO,"led class create success\n");led_device = device_create(led_class,NULL,led_dev,NULL,"led");if(led_device == NULL){printk(KERN_ERR,"device num bind fail\n");goto flag_5;}printk(KERN_INFO,"device num bind sucess\n");if(!request_mem_region(GPJ0_BASE_PA,8,"GPJ0_BASE"))goto flag_6;//pGPJ0_BASE = ioremap(GPJ0_BASE_PA,sizeof(gpj0_reg_t));//pGPJ0_BASE->gpj0con = 0x1111111;baseaddr = ioremap(GPJ0_BASE_PA, 8);writel(0x11111111,baseaddr + S5P_GPJ0CON);led_tmp = (1<<3)|(1<<4)|(1<<5);writel(led_tmp,baseaddr + S5P_GPJ0DAT);return 0;flag_6:device_destroy(led_class,led_dev);flag_5:class_destroy(led_class);flag_4://cdev_put(led_cdev);flag_3:cdev_del(led_cdev);flag_2:unregister_chrdev_region(led_dev,DEV_COUNT);flag_1:return -EINVAL;
}static void __exit led_char_dev_exit()
{//pGPJ0_BASE->gpj0dat = (1<<3)|(1<<4)|(1<<5);//iounmap(pGPJ0_BASE);//release_mem_region(GPJ0_BASE_PA,sizeof(gpj0_reg_t));led_tmp = (1<<3)|(1<<4)|(1<<5);writel(led_tmp,baseaddr + S5P_GPJ0DAT);iounmap(baseaddr);release_mem_region(GPJ0_BASE_PA,8);device_destroy(led_class,led_dev);class_destroy(led_class);//cdev_put(led_cdev);cdev_del(led_cdev);unregister_chrdev_region(led_dev,DEV_COUNT);
}module_init(led_char_dev_init);
module_exit(led_char_dev_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("cxb");
MODULE_DESCRIPTION("led dynamic moudle");
MODULE_ALIAS("led");
Makefile
KERN_DIR = /root/kernelobj-m += led_dynamic_module.oall:make -C $(KERN_DIR) M=`pwd` modules arm-linux-gcc led.c -o ledcp:cp *.ko /root/rootfs/drivercp led /root/rootfs/driver.PHONY: clean
clean:make -C $(KERN_DIR) M=`pwd` modules cleanrm -rf ./led
结果示例: