linux lcd驱动分析五

在前面我们已经明确了LCD驱动其实就是一个字符设备驱动,它的主设备号为29,次设备号同注册的帧缓冲设备有关,从0开始最多支持32个帧缓冲设备。接下来将主要是对LCD这个字符设备的file_operations分析。

struct file_operations定义:

   1424 static const struct file_operations fb_fops = {
   1425         .owner =        THIS_MODULE,
   1426         .read =         fb_read,
   1427         .write =        fb_write,
   1428         .unlocked_ioctl = fb_ioctl,
   1429 #ifdef CONFIG_COMPAT
   1430         .compat_ioctl = fb_compat_ioctl,
   1431 #endif
   1432         .mmap =         fb_mmap,
   1433         .open =         fb_open,
   1434         .release =      fb_release,
   1435 #ifdef HAVE_ARCH_FB_UNMAPPED_AREA
   1436         .get_unmapped_area = get_fb_unmapped_area,
   1437 #endif
   1438 #ifdef CONFIG_FB_DEFERRED_IO
   1439         .fsync =        fb_deferred_io_fsync,
   1440 #endif
   1441 };

先看其中的open和release函数的实现。

   1372 static int
   1373 fb_open(struct inode *inode, struct file *file)
   1374 __acquires(&info->lock)
   1375 __releases(&info->lock)
   1376 {
   1377         int fbidx = iminor(inode);
   1378         struct fb_info *info;
   1379         int res = 0;
   1380
   1381         if (fbidx >= FB_MAX)
   1382                 return -ENODEV;
   1383         info = registered_fb[fbidx];
   1384         if (!info)
   1385                 request_module("fb%d", fbidx);
   1386         info = registered_fb[fbidx];
   1387         if (!info)
   1388                 return -ENODEV;
   1389         mutex_lock(&info->lock);
   1390         if (!try_module_get(info->fbops->owner)) {
   1391                 res = -ENODEV;
   1392                 goto out;
   1393         }
   1394         file->private_data = info;
   1395         if (info->fbops->fb_open) {
   1396                 res = info->fbops->fb_open(info,1);
   1397                 if (res)
   1398                         module_put(info->fbops->owner);
   1399         }

   1400 #ifdef CONFIG_FB_DEFERRED_IO
   1401         if (info->fbdefio)
   1402                 fb_deferred_io_open(info, inode, file);
   1403 #endif
   1404 out:
   1405         mutex_unlock(&info->lock);
   1406         return res;
   1407 }

 在应用程序端,首先要调用open函数打开这个设备。

   1377         int fbidx = iminor(inode);

根据传递进来的struct inode解析出设备的次设备号。

   1381         if (fbidx >= FB_MAX)
   1382                 return -ENODEV;
   1383         info = registered_fb[fbidx];

然后根据这个次设备号找到帧缓冲的struct fb_info结构。

   1394         file->private_data = info;

将这个struct fb_info结构赋值给struct file结构中的一个私有数据区,赋值给它呢是为了以后调用read、write、ioctl等系统调用时找到这个struct fb_info结构。

   1395         if (info->fbops->fb_open) {
   1396                 res = info->fbops->fb_open(info,1);

如果struct fb_info中的struct fb_ops有fb_open函数的话,将调用这个函数,正好我们这里并没有实现这个函数。

open系统调用结束。

   1409 static int
   1410 fb_release(struct inode *inode, struct file *file)
   1411 __acquires(&info->lock)
   1412 __releases(&info->lock)
   1413 {
   1414         struct fb_info * const info = file->private_data;
   1415
   1416         mutex_lock(&info->lock);
   1417         if (info->fbops->fb_release)
   1418                 info->fbops->fb_release(info,1);
   1419         module_put(info->fbops->owner);
   1420         mutex_unlock(&info->lock);
   1421         return 0;
   1422 }

release函数就相对简单的多,如果struct fb_info中的struct fb_ops有fb_release函数的话,将调用这个函数,我们这里也未实现,release函数结束。

这里不去分析字符设备常用的操作read和write函数,因为对于lcd并不是通过这两个函数来访问显存的。

我们这里分配的显存是在内核空间分配的,用户空间并不能直接访问,所以需要用到这里的mmap函数,直接将这段内存空间映射到有用户空间去,用户空间就能访问这段内存空间了。

   1321 static int
   1322 fb_mmap(struct file *file, struct vm_area_struct * vma)
   1323 {
   1324         int fbidx = iminor(file->f_path.dentry->d_inode);
   1325         struct fb_info *info = registered_fb[fbidx];
   1326         struct fb_ops *fb = info->fbops;
   1327         unsigned long off;
   1328         unsigned long start;
   1329         u32 len;
   1330
   1331         if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT))
   1332                 return -EINVAL;
   1333         off = vma->vm_pgoff << PAGE_SHIFT;
   1334         if (!fb)
   1335                 return -ENODEV;
   1336         mutex_lock(&info->mm_lock);
   1337         if (fb->fb_mmap) {
   1338                 int res;
   1339                 res = fb->fb_mmap(info, vma);
   1340                 mutex_unlock(&info->mm_lock);
   1341                 return res;
   1342         }
   1343
   1344         /* frame buffer memory */
   1345         start = info->fix.smem_start;
   1346         len = PAGE_ALIGN((start & ~PAGE_MASK) + info->fix.smem_len);
   1347         if (off >= len) {
   1348                 /* memory mapped io */

   1349                 off -= len;
   1350                 if (info->var.accel_flags) {
   1351                         mutex_unlock(&info->mm_lock);
   1352                         return -EINVAL;
   1353                 }
   1354                 start = info->fix.mmio_start;
   1355                 len = PAGE_ALIGN((start & ~PAGE_MASK) + info->fix.mmio_len);
   1356         }
   1357         mutex_unlock(&info->mm_lock);
   1358         start &= PAGE_MASK;
   1359         if ((vma->vm_end - vma->vm_start + off) > len)
   1360                 return -EINVAL;
   1361         off += start;
   1362         vma->vm_pgoff = off >> PAGE_SHIFT;
   1363         /* This is an IO map - tell maydump to skip this VMA */
   1364         vma->vm_flags |= VM_IO | VM_RESERVED;
   1365         fb_pgprotect(file, vma, off);
   1366         if (io_remap_pfn_range(vma, vma->vm_start, off >> PAGE_SHIFT,
   1367                              vma->vm_end - vma->vm_start, vma->vm_page_prot))
   1368                 return -EAGAIN;
   1369         return 0;
   1370 }

先看这两行:

   1324         int fbidx = iminor(file->f_path.dentry->d_inode);
   1325         struct fb_info *info = registered_fb[fbidx];

还是先得到struct fb_info结构。

mmap映射必须以PAGE_SIZE为单位进行映射,并且被映射的物理内存起始地址也要求是PAGE_SIZE的整数倍。如果区域大小不是页的整数倍,那么映射的区域就比实际显存要大。最后调用io_remap_pfn_range去建立映射。


你可能感兴趣的:(linux lcd驱动分析五)