Block Device Driver in Linux Embedded System Lab. II
Embedded System Lab. II 2
Define objective of applications using device Study Hardware manual (Registers) (vector number) Understand interface to related utilities Device setup Device ioctl command Design to adapt to future changes Module Embedded System Lab. II 3
Linux Device, access. File operation Major number Minor number Device Driver Embedded System Lab. II 4
Application area Application System Call Interface VFS Kernel area Buffer Cache Network Subsystem Char Device Driver Block D/D Network D/D Device Interface Hardware Hardware Embedded System Lab. II 5
Block Device( ) Block device random access mount, Ram Disk, CD-ROM Block device fd* : Floppy disk Hd* : Hard disk sda : SCSI disk brw------- brw-rw---- brw-rw---- brw-rw---- brw-rw---- 1 root floppy 2, 0 May 6 1998 fd0 1 root disk 3, 0 May 6 1998 hda 1 root disk 3, 1 May 6 1998 hda1 1 root disk 8, 0 May 6 1998 sda 1 root disk 8, 1 May 6 1998 sda1 Embedded System Lab. II 6
Block device Block Device Driver Major number - include/linux/major.h 255 : MAX_BLKDEV blkdevs[ ] fs/block_dev.c index block device driver major number driver ~/include/linux/fs.h static struct { const char *name; struct block_device_operations *bdops; } blkdevs[max_blkdev]; /*MAX_BLKDEV = 255*/ Embedded System Lab. II 7
Block device ~/include/linux/fs.h int register_blkdev(unsigned int major, const char *name, struct block_device_operations *bdops); { if (major == 0){ for (major = MAX_BLKDEV-1; major > 0; major--) { if (blkdevs[major].bdops == NULL) { blkdevs[major].name = name; blkdevs[major].bdops = bdops; return major; } } return EBUSY; } if (major >= MAX_BLKDEV) return EINVAL; if (blkdevs[major].bdops && blkdevs[major].bdops!= bdops) return EBUSY; blkdevs[major].name = name; blkdevs[major].bdops = bdops; return 0; } Embedded System Lab. II 8
Block device int unregister_blkdev(unsigned int major, const char *name); { if (major >= MAX_BLKDEV) return EINVAL; if (!blkdevs[major].bdops) return EINVAL; if ( strcmp(blkdevs[major].name, name)) return EINVAL; blkdevs[major].name = NULL; blkdevs[major].bdops = NULL; return 0; } Embedded System Lab. II 9
(1) Block device struct block_device_operations = { int (*open) (struct inode*, struct file*);/*open*/ int (*release) (struct inode*, struct file*, unsigned, unsigned long);/*close*/ int (*ioctl) (struct inode*, struct file*, unsigned, unsigned long);/*i/o control*/ int (*check_media_change) (kdev_t);/* */ int (*revalidate) (kdev_t); /* */ }; Embedded System Lab. II 10
(2) struct file_operations def_blk_fops= { open : blkdev_open, /* */ release : blkdev_close, /* */ llseek : block_llseek, /* */ read : block_read, /* */ write : block_write, /* */ fsync : block_fsync, /* */ ioctl : blkdev_ioctl, /* */ }; Embedded System Lab. II 11
/ blk_dev[] fops blkdevs[] Embedded System Lab. II 12
IDE (1) / (,, ) ( ) ( ) Embedded System Lab. II 13
IDE (2) IDE hd_init() Buffer Cache hd_open( ) hd_release( ) hd_request( ) hd_ioctl( ) hd_interrupt( ) hd_out( ) check_status( ) NULL, block_read, block_write NULL, NULL, hd_ioctl, NULL, hd_open, NULL hd_release, block_fsync struct file_operations hd_ops /* driver/block/hd.c */ Embedded System Lab. II 14
IDE (3) init_process init_module /* driver/block/hd.c */ hd_init() struct file_operations hd_ops /* driver/block/hd.c */ NULL, block_read, block_write NULL, NULL, hd_ioctl, NULL, hd_open, NULL hd_release, block_fsync - register_blkdev(hd_major, hd, &hd_fops); /* fs/devices.c */ register_blkdev() /* include/linux/major.h */ - blkdevs[major].name = device name - blkdevs[major].fops = fops Embedded System Lab. II 15
IDE (4) open /* fs/open.c */ /* fs/namei.c */ open_namei( ) sys_open( ) /* fs/open.c */ filp_open( ) - get_unused_fd( ) - fd_install(fd, f) - struct file initialize - f->f_op->open( ) blkdev_open( ) /* fs/device.c */ - filp->f_op = get_blkfops(major(inode->i_rdev)); /* filp->f_op = blkdevs[major].fops */ - filp->f_op->open; /* hd_open */ Embedded System Lab. II 16
IDE (5) register_chrdev() register_blkdev() blkdevs, / hd_request() (buffer cache) / block_read(), block_write(), Linux. Embedded System Lab. II 17
IDE (6) read /* fs/read_write.c */ sys_read() System call handling layer sock_read() - f->f_op->read hd_read() VFS layer pipe_read() /* mm/filemap.c */ tty_read() Specific File layer generic_file_read() - try to find page in cache, if (hit) OK. - inode->i_op->readpage() Specific FS layer Embedded System Lab. II 18
IDE (7) /* include/linux/blkdev.h */ bread block_read struct blk_dev_struct { request_fn; queue; request;... } blk_dev[]; request_fn struct request { rq_status rq_dev cmd /* R/W */ error sector, nr_sector buffer, bh sem next... } buffer cache ll_rw_block make_request req queue req req request hd_request block device driver hd_out Embedded System Lab. II 19
,, major number read/write read/write read/write (request) Request, read/write.,, Embedded System Lab. II 20
(2) extern int devfs_register_blkdev(unsigned int major, const char name, struct block device operation *bdops) major 0 extern int devfs_unregister_blkdev(unsigned int major, const char *name) read/write/fsync, block_read(), block_write(), block_fsync() open, release ioctl ioctl, Embedded System Lab. II 21
Mini RAMDISK RAM Read/write read/write request,, load RAK Disk Request read/write. - Site : http://www.linuxkernel.net/moduleprog/lkp/ Embedded System Lab. II 22
(1) RAM (minird.c) #include <linux/kernel.h> #include <linux/module.h> #ifdef CONFIG_MODVERSIONS #define MODVERSIONS #include <linux/modversions.h> #endif #include <linux/fs.h> #include <linux/vmalloc.h> #include <asm/uaccess.h> #include <linux/devfs_fs_kernel.h> #define MINIRD_MAJOR 0 /* major number : dynamic allocation */ #define DEVICE_NAME "MiniRD /* device name */ #define DEVICE_FILENAME "minird" #define DEVICE_NUM 2 /* minor device number */ #define MINIRD_DEF_SIZE 1024 /* default device size in KB */ #define MINIRD_BLOCKSIZE 1024 /* sizeof block */ #define MINIRD_SECTSIZE 512 /* sizeof sector */ Embedded System Lab. II 23
(2) extern int s_nmajor; #define MAJOR_NR (s_nmajor) #define DEVICE_NR(device) (MINOR(device)) #define DEVICE_ON(device) /* do nothing */ #define DEVICE_OFF(device) /* do nothing */ #define DEVICE_NO_RANDOM #include <linux/blk.h> #include <linux/blkpg.h> /* Globak Variables */ static int s_nmajor = 0; static int minird_size = MINIRD_DEF_SIZE; static int s_kbsize[device_num]; /* size in blocks of 1024 bytes */ static int s_blocksize[device_num]; /* size of 1024 byte block */ static int s_hardsect[device_num]; /* sizeof real block in bytes */ static int s_length[device_num]; /* size of disks in bytes */ static char *s_data[device_num]; static devfs_handle_t devfs_handle; MODULE_PARM(minird_size, "i"); MODULE_PARM_DESC(minird_size, "RAM Disk size in KB"); Embedded System Lab. II 24
(3) /* Function Prototypes */ static int minird_open(struct inode *inodep, struct file *filp); static int minird_release(struct inode *inodep, struct file *filp); static int minird_ioctl(struct inode *inodep, struct file *filp, unsigned int cmd, unsigned long arg); static int minird_make_request(request_queue_t *q, int rw, struct buffer_head *sbh); /* Device Operations */ static struct block_device_operations minird_fops = { open : minird_open, release : minird_release, ioctl : minird_ioctl, }; /* Module startup/cleanup */ int init_module(void) { int i, j; printk("loading Mini RAM Disk Module\n"); for (i = 0; i < DEVICE_NUM; ++i) { s_kbsize[i] = minird_size; Embedded System Lab. II 25
(4) s_blocksize[i] = MINIRD_BLOCKSIZE; s_hardsect[i] = MINIRD_SECTSIZE; s_length[i] = (minird_size << BLOCK_SIZE_BITS); } if ((s_data[i] = vmalloc(s_length[i])) == NULL) { for (j = 0; j < i; ++j) vfree(s_data[j]); return -ENOMEM; } if ((s_nmajor = devfs_register_blkdev(minird_major, DEVICE_NAME, &minird_fops)) < 0) { printk(device_name " : Device registration failed (%d)\n", s_nmajor); return s_nmajor; } printk(device_name " : Device registered with Major Number = %d\n", MAJOR_NR); devfs_handle = devfs_mk_dir(null, DEVICE_FILENAME, NULL); devfs_register_series(devfs_handle, "%u", DEVICE_NUM, DEVFS_FL_DEFAULT, s_nmajor, 0, S_IFBLK S_IRUGO S_IWUGO, &minird_fops, NULL); blk_queue_make_request(blk_default_queue(major_nr), &minird_make_request); Embedded System Lab. II 26
(5) blk_size[major_nr] = s_kbsize; blksize_size[major_nr] = s_blocksize; hardsect_size[major_nr] = s_hardsect; } return 0; void cleanup_module(void) { int i; printk("unloading Mini RAM Disk Module\n"); for (i = 0; i < DEVICE_NUM; ++i) destroy_buffers(mkdev(major_nr, i)); /* flush the devices */ for (i = 0; i < DEVICE_NUM; ++i) vfree(s_data[i]); devfs_unregister(devfs_handle); devfs_unregister_blkdev(major_nr, DEVICE_NAME); } blk_size[major_nr] = NULL; blksize_size[major_nr] = NULL; hardsect_size[major_nr] = NULL; Embedded System Lab. II 27
(6) /* Device Operations */ int minird_open(struct inode *inodep, struct file *filp) { if (DEVICE_NR(inodep->i_rdev) >= DEVICE_NUM) return -ENXIO; MOD_INC_USE_COUNT; } return 0; int minird_release(struct inode *inodep, struct file *filp) { MOD_DEC_USE_COUNT; return 0; } int minird_ioctl(struct inode *inodep, struct file *filp, unsigned int cmd, unsigned long arg) { int minor = DEVICE_NR(inodep->i_rdev); long devsize; switch (cmd) { case BLKGETSIZE : /* return device size */ devsize = s_length[minor] / s_hardsect[minor]; return put_user(devsize, (long *) arg); Embedded System Lab. II 28
(7) case BLKSSZGET : /* block size of media */ return put_user(s_blocksize[minor], (int *) arg); case BLKFLSBUF : /* flush */ if (!capable(cap_sys_admin)) return -EACCES; destroy_buffers(inodep->i_rdev); break; case BLKROSET : case BLKROGET : return blk_ioctl(inodep->i_rdev, cmd, arg); } default : return -EINVAL; } return 0; /* Request Processing */ int minird_make_request(request_queue_t *q, int rw, struct buffer_head *sbh) { char *ptr, *bdata; int size, minor; Embedded System Lab. II 29
(8) if ((minor = DEVICE_NR(sbh->b_rdev)) > DEVICE_NUM) { printk(device_name " : Unknown Minor Device\n"); goto fail; } ptr = s_data[minor] + sbh->b_rsector * s_hardsect[minor]; size = sbh->b_size; if (ptr + size > s_data[minor] + s_length[minor]) { printk(device_name " : Request past end of device\n"); goto fail; } bdata = bh_kmap(sbh); switch (rw) { case READ : case READA : memcpy(bdata, ptr, size); break; case WRITE : memcpy(ptr, bdata, size); break; default : goto fail; } Embedded System Lab. II 30
(9) sbh->b_end_io(sbh, 1); return 0; fail: } sbh->b_end_io(sbh, 0); return 0; Embedded System Lab. II 31
Makefile /* Makefile */ CC = arm-linux-gcc LD = arm-linux-ld KERNELDIR = /usr/src/linux-2.4.19 INCLUDEDIR = -I$(KERNELDIR)/include CFLAGS = -D KERNEL -DMODULE -O2 -Wall $(INCLUDEDIR) OBJS = minird.o SRCS = minird.c $(OBJS): $(SRCS) $(CC) $(CFLAGS) -c $(SRCS) clean : rm -f *.o *~ Embedded System Lab. II 32
$ make LED minird.o $ insmod minird.o $ mknod /dev/minird0 b [major] 0 $ dd if=/dev/zero of=/dev/minird0 bs=1024 count=1024 $ mke2fs /dev/minird0 1024 $ mkdir /mnt/other $ mount /dev/minird0 /mnt/other $ cp /bin/ls /mnt/other $ ls /mnt/other $ umount /mnt/other $ ls /mnt/other $ mount /dev/minird0 /mnt/other $ ls /mnt/other Embedded System Lab. II 33
Embedded System Lab. II 34
(2) Embedded System Lab. II 35
(3) Embedded System Lab. II 36