Chinaunix首页 | 论坛 | 博客
  • 博客访问: 74627
  • 博文数量: 22
  • 博客积分: 1475
  • 博客等级: 上尉
  • 技术积分: 260
  • 用 户 组: 普通用户
  • 注册时间: 2010-03-04 23:39
文章分类

全部博文(22)

文章存档

2013年(1)

2011年(6)

2010年(15)

我的朋友

分类: LINUX

2010-05-17 16:50:17


先不要考滤字符设备如何和文件系统接上头,最后说。
相关结构。
struct probe
{
  struct probe *next;
  //设备号。
  dev_t dev;
  //设备号的范围。
  unsigned long range;
  struct module *owner;
  //如果是块设备返回struct gendisk(struct cdev 如果是字符设备)对应的struct kobject.
  kobj_probe_t *get;
  int (*lock)(dev_t, void *);
  //设备的私有数据,块设备为struct gendisk,字符设备为struct cdev.
  void *data;
};
//队像位图哈希表,最多255个表项,是主设备号%255.
//每个表项以range从小到大排序,每一项代表一个设备,一个设备对应多个主 probe.
struct kobj_map
{
 struct probe *   probes[255];
 struct semaphore * sem;
};
这个kobj_map的作用就是记录设备号的,当打开字符设备时根据设备号找到struct probe之后再返回里面的data,这个data就是struct cdev.
struct cdev {
 struct kobject kobj;
 struct module *owner;
 //操作具体设备的函数集。
 struct file_operations *ops;
 //所有字符设备的I节点头。
 struct list_head list;
 //开始设备号(主+次)。
 dev_t dev;
 //设备数量
 unsigned int count;
};
这就代表一个主设备号的字符设备。
//字符设备散列表,共有255个散列表项,每一项代表一个主设备
//发生冲突时用next链接,按设备号从小到大排列。
static struct char_device_struct {
 struct char_device_struct *next;
 //主设备号。
 unsigned int major;
 //次设备号。
 unsigned int baseminor;
 //范围。
 int minorct;
 //主设备名字
 const char *name;
 struct file_operations *fops;
 struct cdev *cdev;  /* will die */
} *chrdevs[MAX_PROBE_HASH];
这是注册字符设备的一个中间结构,没看出来它有什么用。
//创建一个哈希表位图,代表所有字符设备的位图。
void __init chrdev_init(void)
{
 cdev_map = kobj_map_init(base_probe, &chrdevs_lock);
}
1、注册字符设备1。
//这个函数就是以major为注设备号,次设备号从0到255,来注册一个字符设备驱动程序。
int register_chrdev(unsigned int major, const char *name,
      struct file_operations *fops)
{
 struct char_device_struct *cd;
 struct cdev *cdev;
 char *s;
 int err = -ENOMEM;
 cd = __register_chrdev_region(major, 0, 256, name);
 if (IS_ERR(cd))
  return PTR_ERR(cd);
 //分配struct cdev
 cdev = cdev_alloc();
 if (!cdev)
  goto out2;
 //成员赋值。
 cdev->owner = fops->owner;
 cdev->ops = fops;
 kobject_set_name(&cdev->kobj, "%s", name);
 for (s = strchr(kobject_name(&cdev->kobj),'/'); s; s = strchr(s, '/'))
  *s = '!';
 //注册到字符设备哈希表。
 err = cdev_add(cdev, MKDEV(cd->major, 0), 256);
 if (err)
  goto out;
 cd->cdev = cdev;
 return major ? 0 : cd->major;
out:
 kobject_put(&cdev->kobj);
out2:
 kfree(__unregister_chrdev_region(cd->major, 0, 256));
 return err;
}
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 = kmalloc(sizeof(struct char_device_struct), GFP_KERNEL);
 if (cd == NULL)
  return ERR_PTR(-ENOMEM);
 memset(cd, 0, sizeof(struct char_device_struct));
 down(&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;
 cd->name   = name;
 //转成表项。
 i = major_to_index(major);
 //确定哈希表项,在表项列表里找一个没有使作的范围。
 for (cp = &chrdevs[i]; *cp; cp = &(*cp)->next)
  if ((*cp)->major > major || ((*cp)->major == major && (*cp)->baseminor >= baseminor))
   break;
 //请求的设备号已经存在。
 if (*cp && (*cp)->major == major &&
     (*cp)->baseminor < baseminor + minorct) {
  ret = -EBUSY;
  goto out;
 }
 //加到表项里。
 cd->next = *cp;
 *cp = cd;
 up(&chrdevs_lock);
 return cd;
out:
 up(&chrdevs_lock);
 kfree(cd);
 return ERR_PTR(ret);
}
int cdev_add(struct cdev *p, dev_t dev, unsigned count)
{
 p->dev = dev;
 p->count = count;
 return kobj_map(cdev_map, dev, count, NULL, exact_match, exact_lock, p);
}
int kobj_map(struct kobj_map *domain, dev_t dev, unsigned long range,
      struct module *module, kobj_probe_t *probe,
      int (*lock)(dev_t, void *), void *data)
{
 //0xfff fffff
 //计算请求范围占用几个主设备号。
 unsigned n = MAJOR(dev + range - 1) - MAJOR(dev) + 1;
 //主设备号
 unsigned index = MAJOR(dev);
 unsigned i;
 struct probe *p;
 if (n > 255)
  n = 255;
 p = kmalloc(sizeof(struct probe) * n, GFP_KERNEL);
 if (p == NULL)
  return -ENOMEM;
 for (i = 0; i < n; i++, p++) {
  p->owner = module;
  p->get = probe;
  p->lock = lock;
  p->dev = dev;
  p->range = range;
  p->data = data;
 }
 
 down(domain->sem);
 for (i = 0, p -= n; i < n; i++, p++, index++)
 {
  //从小到大排序。
  struct probe **s = &domain->probes[index % 255];
  while (*s && (*s)->range < range)
   s = &(*s)->next;
  p->next = *s;
  *s = p;
 }
 up(domain->sem);
 return 0;
}
1、注册字符设备3。
这个让系统自动分配一个主设备号,次设备号的大小和数量由参数传入,好像很节约啊,当然这只是分配了设备号还没加到map里。
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;
}
1、注册字符设备3。
这个就是成批的注册,from是初始的设备号(注+次)count是数量。
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);
}
2、打开设备。
当sys_open打开一个字符设备文件时会把def_chr_fops赋给inode的i_fop,接着会调用def_chr_fops的int chrdev_open(struct inode *
inode, struct file * filp),也就是上面的函数,chardev_open会根据inode里的i_cdev也就是设备号到cdev_map里找相应的cdev,然后
filp->f_op = cdev->ops(这回把字符设备的文件操作结构都赋给了file结构,以后的读写就用这里面的函数了),再调用filp->f_op->open就
把设备文件打开了,注意这具cdev里的ops并不一定就是最终的设备打开函数,下面从纵向下会有更多的驱动层次,从横向上会根据次设备号
的不同调用不同的驱动(framebuffer就是一个例子)。
struct file_operations def_chr_fops = {
 .open = chrdev_open,
};
int chrdev_open(struct inode * inode, struct file * filp)
{
 struct cdev *p;
 struct cdev *new = NULL;
 int ret = 0;
 spin_lock(&cdev_lock);
 p = inode->i_cdev;
 if (!p)
 {
  struct kobject *kobj;
  int idx;
  spin_unlock(&cdev_lock);
  //找cdev
  kobj = kobj_lookup(cdev_map, inode->i_rdev, &idx);
  if (!kobj)
   return -ENXIO;
  new = container_of(kobj, struct cdev, kobj);
  spin_lock(&cdev_lock);
  p = inode->i_cdev;
  //如果第一次打开I节点里没有CDEV?
  if (!p)
  {
   inode->i_cdev = p = new;
   inode->i_cindex = idx;
   list_add(&inode->i_devices, &p->list);
   new = NULL;
  } else if (!cdev_get(p))
   ret = -ENXIO;
 }
 else if (!cdev_get(p))
  ret = -ENXIO;
 spin_unlock(&cdev_lock);
 cdev_put(new);
 if (ret)
  return ret;
 //转手为具体设备的操作集。
 filp->f_op = fops_get(p->ops);
 if (!filp->f_op) {
  cdev_put(p);
  return -ENXIO;
 }
 //调用具体的open函数。
 if (filp->f_op->open) {
  lock_kernel();
  ret = filp->f_op->open(inode,filp);
  unlock_kernel();
 }
 if (ret)
  cdev_put(p);
 return ret;
}
//根据设备号查找设备队像的kobject
struct kobject *kobj_lookup(struct kobj_map *domain, dev_t dev, int *index)
{
 struct kobject *kobj;
 struct probe *p;
 unsigned long best = ~0UL;
retry:
 down(domain->sem);
 for (p = domain->probes[MAJOR(dev) % 255]; p; p = p->next) {
  struct kobject *(*probe)(dev_t, int *, void *);
  struct module *owner;
  void *data;
  if (p->dev > dev || p->dev + p->range - 1 < dev)
   continue;
  if (p->range - 1 >= best)
   break;
  if (!try_module_get(p->owner))
   continue;
  owner = p->owner;
  data = p->data;
  probe = p->get;
  best = p->range - 1;
  //返回次设备号。
  *index = dev - p->dev;
  if (p->lock && p->lock(dev, data) < 0) {
   module_put(owner);
   continue;
  }
  up(domain->sem);
  kobj = probe(dev, index, data);
  /* Currently ->owner protects _only_ ->probe() itself. */
  module_put(owner);
  if (kobj)
   return kobj;
  goto retry;
 }
 up(domain->sem);
 return NULL;
}
 
阅读(1067) | 评论(0) | 转发(0) |
0

上一篇:(6)linux设备管理

下一篇:(8)linux块设备

给主人留下些什么吧!~~