6.3. ¸ðµâ µ¿ÀÛÀÇ ÀÌÇØ

¸ðµâÀº µî·ÏµÉ ¶§ µð¹ÙÀ̽º ¹øÈ£¸¦ µî·ÏÇϰí ÀÌ¿Í ÇÔ²² file_operations ¶ó´Â ±¸Á¶Ã¼¸¦ Ä¿³Î¿¡ ¾Ë·ÁÁØ´Ù. file_operations´Â include/linux/fs.h¿¡ Á¤ÀǵǾî ÀÖ°í ´ÙÀ½°ú °°´Ù.

/*
 * NOTE:
 * read, write, poll, fsync, readv, writev can be called
 *   without the big kernel lock held in all filesystems.
 */
struct file_operations {
	struct module *owner;
	loff_t (*llseek) (struct file *, loff_t, int);
	ssize_t (*read) (struct file *, char *, size_t, loff_t *);
	ssize_t (*write) (struct file *, const char *, size_t, loff_t *);
	int (*readdir) (struct file *, void *, filldir_t);
	unsigned int (*poll) (struct file *, struct poll_table_struct *);
	int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);
	int (*mmap) (struct file *, struct vm_area_struct *);
	int (*open) (struct inode *, struct file *);
	int (*flush) (struct file *);
	int (*release) (struct inode *, struct file *);
	int (*fsync) (struct file *, struct dentry *, int datasync);
	int (*fasync) (int, struct file *, int);
	int (*lock) (struct file *, int, struct file_lock *);
	ssize_t (*readv) (struct file *, const struct iovec *, unsigned long, loff_t *);
	ssize_t (*writev) (struct file *, const struct iovec *, unsigned long, loff_t *);
	ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);
	unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
};

¸ðµç µð¹ÙÀ̽º µå¶óÀ̹ö´Â »ç¿ëÀÚ°¡ file_operations¸¦ »ç¿ëÇØ µî·ÏÇØÁØ Ç¥ÁØÈ­µÇ¾î ÀÖ´Â ÀÎÅÍÆäÀ̽º ¸¦ »ç¿ëÇØ ÀÔ/Ãâ·Â µîÀÇ ÀÏÀ» ÇϰԵȴÙ. À¯´Ð½º¿¡¼­´Â µð¹ÙÀ̽º³ª ³×Æ®¿÷À̳ª ¸ðµÎ ÇϳªÀÇ ÆÄÀÏ Ã³·³ µ¿ÀÛÇϵµ·Ï µÇ¾î Àִµ¥ ÀÌ¿¡ µû¸¥ ÇÔ¼öµéÀÌ µî·ÏµÇ¾î ÀÖ´Ù. ¿¹¸¦ µé¾î µð¹ÙÀ̽º·ÎºÎÅÍ Àб⠵¿ÀÛÀ» ¿øÇÑ´Ù¸é file_operations¿¡ µî·ÏµÈ read ÇÔ¼ö¸¦ »ç¿ëÇØ Àб⸦ ÇÑ´Ù.

file_operations´Â ¸ðµÎ ä¿ï ÇÊ¿ä´Â ¾ø´Ù. ÇÊ¿äÇϰųª Áö¿øµÇ¾ßÇÏ´Â °ÍÀ» ä¿ö ³ÖÀ¸¸é µÈ´Ù. ±×·¯³ª ¹ü¿ëÀûÀÎ µð¹ÙÀ̽º¸¦ ¸¸µç´Ù¸é µÇµµ·Ï ¸ðµç ÇÔ¼ö¸¦ ´Ù ä¿ö³Ö¾î ÁÖ´Â °ÍÀÌ ÁÁÀ» °ÍÀÌ´Ù.

±×·¯³ª file_operations¿¡ Á¸ÀçÇÏ´Â ÇÔ¼öÀÇ °³¼ö¿¡ Á¦¾àÀÌ ÀÖÀ¸¹Ç·Î µð¹ÙÀ̽º¿¡ ´ëÇØ file_operations ¿ÜÀÇ ´Ù¸¥ ±â´É ȤÀº ÇÔ¼ö¸¦ ¿øÇÏ´Â °æ¿ì¿£ ioctlÀ» »ç¿ëÇÑ´Ù. ioctlÀÇ C ¶óÀ̺귯¸® ³»ÀÇ Á¤ÀÇ´Â ´ÙÀ½°ú °°ÀÌ µÇ¾î ÀÖ´Ù.

#include <sys/ioctl.h>

int ioctl(int d, int request, ...)

ioctl ÇÔ¼ö¸¦ »ç¿ëÇÒ ¶§ request¶õ ¼ýÀÚ¸¦ Àü´ÞÇØ Áִµ¥ ÀÌ °ÍÀÌ ioctl¿¡ ÀÇÇØ ºÒ¸®´Â ÇÔ¼öÀÇ À妽º°¡ µÈ´Ù. Áï ioctl·Î ºÒ¸®´Â ÇÔ¼ö´Â switch ¹®°ú °°Àº °ÍÀ» ÀÌ¿ëÇØ request·Î Àü´ÞµÈ °ªÀ» ºñ±³ÇØ ÇØ´ç ÇÔ¼ö¸¦ ´Ù½Ã È£ÃâÇØ ÁÖ°Ô µÈ´Ù. ´ÙÀ½ ¼Ò½º´Â ÇÏµå µð½ºÅ©ÀÇ ½Ã¸®¾ó ¹øÈ£¸¦ ÀÐ¾î ³»´Â ±â´ÉÀ» ÇÏ´Â µð¹ÙÀ̽º µå¶óÀ̹ö¸¦ ¸¸µé¾î º» °ÍÀÌ´Ù.

/* hddinfo.c */
#ifndef __KERNEL__
#define __KERNEL__
#endif
#ifndef MODULE
#define MODULE
#endif

#define __NO_VERSION__
#include <linux/module.h>
#include <linux/version.h>
#include <linux/errno.h>
#include <linux/fs.h>
#include <linux/ide.h>
#include <asm/uaccess.h>

#define HDA			0x0300
#define HARDDISK	HDA

char kernel_version[] = UTS_RELEASE;

static int hddinfo_open(struct inode *node, struct file *f)
{
	return 0;
}

static int hddinfo_release(struct inode *node, struct file *f)
{
	return 0;
}

static ssize_t hddinfo_read(struct file *f, char *buf, size_t nbytes, loff_t *ppos)
{
	return nbytes;
}

static ssize_t read_serial(char *dst)
{
	ide_drive_t *drv;

	drv = get_info_ptr(HARDDISK);
	if (drv)
		copy_to_user(dst, drv->id->serial_no, 20);
	else
	{
		;//PDEBUG("HDDINFO : Cannot get the drive information\n");
		return 0;
	}

	return 20;
}

int hddinfo_ioctl(struct inode *node, struct file *f, unsigned int ioctl_num, unsigned long ioctl_param)
{
	switch (ioctl_num)
	{
		case 0 :
			read_serial((char *)(ioctl_param));
			break;
	}

	return 0;
}

struct file_operations Fops = {
	NULL,
	NULL,
	hddinfo_read,
	NULL,
	NULL,
	NULL,
	hddinfo_ioctl,
	NULL,
	hddinfo_open,
	NULL,
	hddinfo_release
};

int init_module()
{
	if (register_chrdev(212, "hddinfo", &Fops) < 0)
	{
		//PDEBUG("HDDINFO : Unable to register driver.\n");
		return -EIO;
	}

	return 0;
}

void cleanup_module()
{
	if (unregister_chrdev(212, "hddinfo") < 0)
		;//PDEBUG("HDDINFO : Unable to unregister\n");
}

hddinfo.c¿¡¼­ Á¤ÀÇµÈ file_operations´Â read/ioctl/open/release ¸¸À» »ç¿ëÇÑ´Ù. open°ú release´Â ÀÌ µð¹ÙÀ̽º¸¦ open/closeÇÒ ¶§ ºÒ¸®¹Ç·Î µð¹ÙÀ̽º¸¦ »ç¿ëÇϱâ Àü¿¡ ÃʱâÈ­ ÇØ¾ßÇϰųª ȤÀº »ç¿ëÀ» ÁßÁöÇϱâ Àü¿¡ ¶Ç ÇÊ¿äÇÑ ÀÛ¾÷À» ÇØ¾ßÇÏ´Â °æ¿ì ÀÌ ÇÔ¼öµé¿¡ ÇÊ¿äÇÑ ±â´ÉÀ» ³ÖÀ¸¸é µÈ´Ù. hddinfo.c ¿¡¼­´Â open/close¿¡ µû¸¥ ÀÛ¾÷À» ÇÒ Çʿ䰡 ¾ø¾î ¾Æ¹«°Íµµ ³ÖÁö ¾Ê°í ±×³É 0À» ¸®ÅÏÇÏ´Â ±â´ÉÀ» ³Ö¾î ¿¹Á¦·Î ¿Ã·È´Ù. read¸¦ »ç¿ëÇØ Çϵåµð½ºÅ©ÀÇ ½Ã¸®¾ó ¹øÈ£¸¦ Àеµ·Ï ÇØµµ µÇÁö¸¸ ¿©±â¼± ioctlÀÇ »ç¿ëÀ» º¸±âÀ§ÇØ ÀϺη¯ read¿¡¼­ ÇÒ ÀÐÀ» ioctl·Î »Ì¾Æ ¸¸µé¾î ºÃ´Ù.

ÀÌ ¸ðµâÀÇ »ç¿ëÀº ¾Æ·¡¿Í °°Àº ÇÁ·Î±×·¥À¸·Î µ¿ÀÛ ½ÃŲ´Ù.

#include <stdio.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <stdlib.h>
#include <fcntl.h>

int main()
{
	int fd;
	char buf[256];

	fd = open("/dev/hdd_info", O_RDWR);
	if (fd < 0)
	{
		printf("Device open error.\n");
		return -1;
	}

	ioctl(fd, 0, buf);

	printf("buf : %s\n", buf);

	close(fd);

	return 0;
}

test.c¸¦ µ¿ÀÛ ½Ã۱â Àü¿¡ /dev/hdd_info¸¦ ¸¸µé¾î Áà¾ßÇÑ´Ù. 'mknod c 212 0 /dev/hdd_info'·Î ¸¸µé¾î ÁØ´Ù. ÀϹÝÀûÀ¸·Î µð¹ÙÀ̽º¸¦ »ç¿ëÇϱâ À§ÇØ µð¹ÙÀ̽º ÆÄÀÏÀ» »ç¿ëÇØ µð¹ÙÀ̽º¸¦ open ÇÑ´Ù. ¿©±â¿¡¼­ ¾ò¾îÁö´Â ÇÚµéÀ» »ç¿ëÇØ µð¹ÙÀ̽º¿¡ ÀÐ°í ¾²±â µ¿ÀÛ µîÀ» ÇÑ ÈÄ ´Ù »ç¿ëÇßÀ¸¸é close·Î »ç¿ëÀ» ÁßÁöÇØ ÁØ´Ù. ÀÌ ÀýÂ÷°¡ ÀüÇü ÀûÀÎ ÀýÂ÷¿¡ ÇØ´çÇÑ´Ù.

test.c¿¡¼­µµ open ÈÄ ioctlÀÇ 0¹ø ÇÔ¼ö¸¦ È£ÃâÇØ hddinfo.cÀÇ read_serial()À» ºÒ·¯ Çϵåµð½ºÅ©ÀÇ ½Ã¸®¾ó ¹øÈ£¸¦ Àоî¿Â´Ù. Çϵåµð½ºÅ©ÀÇ ½Ã¸®¾ó ¹øÈ£´Â Ä¿³Î ºÎÆÃÇÒ ¶§ ÀÌ¹Ì ¾ò¾îÁø Çϵåµð½ºÅ©¿¡ ´ëÇÑ Á¤º¸¸¦ °®°í ÀÖ´Â ±¸Á¶Ã¼¿¡¼­ º¹»çÇÑ´Ù.