在前面我们已经明确了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去建立映射。