1 / 40

chapter 3- Char Device Driver

chapter 3- Char Device Driver. chenbo2008@ustc.edu.cn 中国科学技术大学软件学院. ls -l /dev. Example from the text. You can download example for the text at http://examples.oreilly.com/linuxdrive3/. 加载. 卸载. The script to load the built modules. scull_load Remember: make it executable first.

annaw
Download Presentation

chapter 3- Char Device Driver

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. chapter 3-Char Device Driver chenbo2008@ustc.edu.cn 中国科学技术大学软件学院

  2. ls -l /dev

  3. Example from the text You can download example for the text at http://examples.oreilly.com/linuxdrive3/ 加载 卸载

  4. The script to load the built modules scull_load Remember: make it executable first. Runs it with superuser’s privilege. #!/bin/sh # $Id: scull_load,v 1.4 2004/11/03 06:19:49 rubini Exp $ module="scull" device="scull" mode="664" # Group: since distributions do it differently, look for wheel or use staff if grep -q '^staff:' /etc/group; then group="staff" else group="wheel" fi simple character utility for loading localities

  5. The script to load the built modules # invoke insmod with all arguments we got # and use a pathname, as insmod doesn't look in . by default /sbin/insmod ./$module.ko $* || exit 1 # retrieve major number major=$(awk "\$2==\"$module\" {print \$1}" /proc/devices) # Remove stale nodes and replace them, then give gid and perms # Usually the script is shorter, it's scull that has several devices in it. rm -f /dev/${device}[0-3] mknod /dev/${device}0 c $major 0 mknod /dev/${device}1 c $major 1 mknod /dev/${device}2 c $major 2 mknod /dev/${device}3 c $major 3 ln -sf ${device}0 /dev/${device} chgrp $group /dev/${device}[0-3] chmod $mode /dev/${device}[0-3] rm -f /dev/${device}pipe[0-3] mknod /dev/${device}pipe0 c $major 4 mknod /dev/${device}pipe1 c $major 5 mknod /dev/${device}pipe2 c $major 6 mknod /dev/${device}pipe3 c $major 7 ln -sf ${device}pipe0 /dev/${device}pipe chgrp $group /dev/${device}pipe[0-3] chmod $mode /dev/${device}pipe[0-3] Scull_load

  6. The script to load the built modules rm -f /dev/${device}single mknod /dev/${device}single c $major 8 chgrp $group /dev/${device}single chmod $mode /dev/${device}single rm -f /dev/${device}uid mknod /dev/${device}uid c $major 9 chgrp $group /dev/${device}uid chmod $mode /dev/${device}uid rm -f /dev/${device}wuid mknod /dev/${device}wuid c $major 10 chgrp $group /dev/${device}wuid chmod $mode /dev/${device}wuid rm -f /dev/${device}priv mknod /dev/${device}priv c $major 11 chgrp $group /dev/${device}priv chmod $mode /dev/${device}priv Scull_load

  7. lrwxrwxrwx 1 root root 6 2007-03-27 06:52 scull -> scull0 crw-rw-r-- 1 root wheel 254, 0 2007-03-27 06:52 scull0 crw-rw-r-- 1 root wheel 254, 1 2007-03-27 06:52 scull1 crw-rw-r-- 1 root wheel 254, 2 2007-03-27 06:52 scull2 crw-rw-r-- 1 root wheel 254, 3 2007-03-27 06:52 scull3 lrwxrwxrwx 1 root root 10 2007-03-27 06:52 scullpipe -> scullpipe0 crw-rw-r-- 1 root wheel 254, 4 2007-03-27 06:52 scullpipe0 crw-rw-r-- 1 root wheel 254, 5 2007-03-27 06:52 scullpipe1 crw-rw-r-- 1 root wheel 254, 6 2007-03-27 06:52 scullpipe2 crw-rw-r-- 1 root wheel 254, 7 2007-03-27 06:52 scullpipe3 crw-rw-r-- 1 root wheel 254, 11 2007-03-27 06:52 scullpriv crw-rw-r-- 1 root wheel 254, 8 2007-03-27 06:52 scullsingle crw-rw-r-- 1 root wheel 254, 9 2007-03-27 06:52 sculluid crw-rw-r-- 1 root wheel 254, 10 2007-03-27 06:52 scullwuid The script to load the built modules

  8. The script to load/unload the built modules scull_unload Again, make it executable and run as superuser. #!/bin/sh module="scull" device="scull" # invoke rmmod with all arguments we got /sbin/rmmod $module $* || exit 1 # Remove stale nodes rm -f /dev/${device} /dev/${device}[0-3] rm -f /dev/${device}priv rm -f /dev/${device}pipe /dev/${device}pipe[0-3] rm -f /dev/${device}single rm -f /dev/${device}uid rm -f /dev/${device}wuid

  9. The script to load the built modules • Being a character device, you can read/write character string to it. • #echo “This is a test” > /dev/scull • #cat < /dev/scull • The text use the example scull to illustrate all necessary ingredients in developing character device driver. How to create an character device and hook it up the a device file? Involves two passes of operation • To the kernel, initiates the device and get/announce the major-minor number. • To the user program, creates a file inside the directory /dev.

  10. dev_t Within the kernel, device numbers is of dev_t type(defined in <include/linux/types.h> ). As of Version 2.6.0 of the kernel, dev_t is a 32-bitquantity with 12 bits set aside for the major number and 20 bits for the minor number. A prior to 2.6.x, size of major and minor are both 8bits.

  11. Device Number type dev_t Macros in retrieving device numbers: MAJOR(dev_t dev); MINOR(dev_t dev); Macros in wraping device numbers: MKDEV(int major, int minor); Defined in <linux/kdev_t.h>. <linux/kdev_t.h> 3 #ifdef __KERNEL__ 4 #define MINORBITS 20 5 #define MINORMASK ((1U << MINORBITS) - 1) 6 7 #define MAJOR(dev) ((unsigned int) ((dev) >> MINORBITS)) 8 #define MINOR(dev) ((unsigned int) ((dev) & MINORMASK)) 9 #define MKDEV(ma,mi) (((ma) << MINORBITS) | (mi))

  12. Allocating and Freeing Device Numbers Adding a driver to the system you will have to first register it to the kernel.dev_t is required for registration.How to get the value? Statically and dynamically. Using following functions: Defined as function in: fs/char_dev.c Defined as function prototype in: include/linux/fs.h * fs/char_dev.c */ 1289 extern int alloc_chrdev_region(dev_t *, unsigned, unsigned, const char *); 1290 extern int register_chrdev_region(dev_t, unsigned, const char *); 1291 extern int register_chrdev(unsigned int, const char *, 1292 struct file_operations *); 1293 extern int unregister_chrdev(unsigned int, const char *); 1294 extern void unregister_chrdev_region(dev_t, unsigned);

  13. Allocating and Freeing Device Numbers Having a static device number, you can assign the major number during the module’s initialization. using register_chrdev() or register_chrdev_region() • int register_chrdev(unsigned int major, const * char name,operation *fops); • major -The major number for the driver. • name -The name of the driver (as seen in /proc/devices). • fops -The &file_operations structure pointer. • Noticed, no minor number is required. • int register_chrdev_region(dev_t first,unsigned int count, char *name); • first : the beginning device number of the range. • count : the requesting number of contiguous devices. • name : the name of the device (will appear in/proc/devices and sysfs.) • Return 0 if successful. register_chrdev_region(dev_num,2,"my_dev")

  14. for (cp = &chrdevs[i]; *cp; cp = &(*cp)->next) if ((*cp)->major > major || ((*cp)->major == major && ( ((*cp)->baseminor >= baseminor) || ((*cp)->baseminor + (*cp)->minorct > baseminor)) )) break; /* Check for overlapping minor ranges. */ if (*cp && (*cp)->major == major) { int old_min = (*cp)->baseminor; int old_max = (*cp)->baseminor + (*cp)->minorct - 1; int new_min = baseminor; int new_max = baseminor + minorct - 1; /* New driver overlaps from the left. */ if (new_max >= old_min && new_max <= old_max) { ret = -EBUSY; goto out; } /* New driver overlaps from the right. */ if (new_min <= old_max && new_min >= old_min) { ret = -EBUSY; goto out; } } cd->next = *cp; *cp = cd; mutex_unlock(&chrdevs_lock); return cd; out: mutex_unlock(&chrdevs_lock); kfree(cd); return ERR_PTR(ret); } 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 = kzalloc(sizeof(struct char_device_struct), GFP_KERNEL); if (cd == NULL) return ERR_PTR(-ENOMEM); mutex_lock(&chrdevs_lock); 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; strncpy(cd->name,name, 64); i = major_to_index(major);

  15. Allocating and Freeing Device Numbers • Linux kernel dynamically allocates a major number using function: alloc_chrdev_region() alloc_chrdev_region() int alloc_chrdev_region(dev_t *dev, unsigned int firstminor,unsigned int count, char *name); dev: return the first number in your allocated range on successful completion. firstminor: the requested first minor number to use; it is usually0. count and name: work like those giventoregister_chrdev_region. 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; }

  16. Allocating and Freeing Device Numbers • Device numbers are freed by: • unregister_chrdev_region()or • unregister_chrdev(). • Call these functions in your module’s cleanup function. • unregister_chrdev(). • int unregister_chrdev (unsigned int major, const char * name); • major : major number for the driver. • name : name of the driver (as seen in /proc/devices). void unregister_chrdev(unsigned int major, const char *name) { struct char_device_struct *cd; cd = __unregister_chrdev_region(major, 0, 256); if (cd && cd->cdev) cdev_del(cd->cdev); kfree(cd); }

  17. Allocating and Freeing Device Numbers • unregister_chrdev_region(). • int unregister_chrdev_region(dev_t from, unsigned count, const char *name) • from: the requested first minor number to use, it is usually 0. • count and name: work like those given to register_chrdev_region. void unregister_chrdev_region(dev_t from, unsigned count) { 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; kfree(__unregister_chrdev_region(MAJOR(n), MINOR(n), next - n)); } }

  18. __unregister_chrdev_region static struct char_device_struct * __unregister_chrdev_region(unsigned major, unsigned baseminor, int minorct) { struct char_device_struct *cd = NULL, **cp; int i = major_to_index(major); mutex_lock(&chrdevs_lock); for (cp = &chrdevs[i]; *cp; cp = &(*cp)->next) if ((*cp)->major == major && (*cp)->baseminor == baseminor && (*cp)->minorct == minorct) break; if (*cp) { cd = *cp; *cp = cd->next; } mutex_unlock(&chrdevs_lock); return cd; }

  19. Installation of a driver with dynamic allocated majornumbers • The problem: since it is dynamical-allocated, themajor number assigned to the module is unknownand unable to create the device nodes (files) inadvance. • Using information inside of /proc/devices • However, since once the number has been assigned, it isregistered in the/proc/devices. Consequently, using asimple shell script can retrieving the major number, andcreate the corresponding nodes in /dev. • Take script scull_load for example.

  20. Installation of a driver with dynamic allocated majornumbers

  21. The script to creates nodes in /dev #!/bin/sh module="scull" device="scull" mode="664" # invoke insmod with all arguments we got # and use a pathname, as newer modutils don't look in . by default # this will generate new entries in /proc/devices. The $* is the positional # parameter to pass command line parameters to insmod. /sbin/insmod ./$module.ko $* || exit 1 # remove stale nodes rm -f /dev/${device}[0-3] Installation of a driver with dynamic allocated majornumbers

  22. The script to creates nodes in /dev • # Retrieve major number form /proc/devices using awk • # Noticed that the script form the text has bugs, i.e. it should be \$2 instead of • # \\$2, same to the • # entry \$1. The awk scan /proc/devices for pattern “\$module\”, • # if it find it, the first item of the scanned line is “printed.” • major=$(awk "\$2= =\"$module\" {print \$1}" /proc/devices) • # Use mknod to create corresponding devices in /dev. • # Since a total of 4 scull character “c” devices are created, mknod is invoked 4 times. • mknod /dev/${device}0 c $major 0 • mknod /dev/${device}1 c $major 1 • mknod /dev/${device}2 c $major 2 • mknod /dev/${device}3 c $major 3 Installation of a driver with dynamic allocated majornumbers

  23. The script to creates nodes in /dev • # give appropriate group/permissions, and change the group. • # Not all distributions have staff, some have "wheel" instead. • # It doesn’t have to be staff or whell, it could be anything you want. • group="staff“ • # If “staff” is un-defined, use wheel instead. • grep -q '^staff:' /etc/group || group="wheel“ • # File name expansion of class [0-3] is performed here. • chgrp $group /dev/${device}[0-3] • chmod $mode /dev/${device}[0-3] Installation of a driver with dynamic allocated majornumbers

  24. The scull provides both ways: the major –minornumber can be either – Dynamically allocated orload/compile time assigned In line 615 of main.c int scull_init_module(void) : if (scull_major) { /*value of scull_major is assigned at scull.h to be 0 dev = MKDEV(scull_major, scull_minor); result = register_chrdev_region(dev, scull_nr_devs, "scull"); } else { result = alloc_chrdev_region(&dev, scull_minor, scull_nr_devs, "scull"); scull_major = MAJOR(dev); } The scull do it both ways

  25. By setting the major number in scull.h Compile time In line 615 of main.c int scull_init_module(void) : if (scull_major) { /*value of scull_major is assigned at scull.h to be 0 dev = MKDEV(scull_major, scull_minor); result = register_chrdev_region(dev, scull_nr_devs, "scull"); } else { result = alloc_chrdev_region(&dev, scull_minor, scull_nr_devs, "scull"); scull_major = MAJOR(dev); } The scull do it both ways In scull.h line 47-49 #ifndef SCULL_MAJOR #define SCULL_MAJOR 0 /* dynamic major by default */ #endif

  26. By setting the major number in scull.h Compile time In line 42 of main.c int scull_major = SCULL_MAJOR; : In line 615 of main.c int scull_init_module(void) : if (scull_major) { /*value of scull_major is assigned at scull.h to be 0 dev = MKDEV(scull_major, scull_minor); result = register_chrdev_region(dev, scull_nr_devs, "scull"); } else { result = alloc_chrdev_region(&dev, scull_minor, scull_nr_devs, "scull"); scull_major = MAJOR(dev); } The scull do it both ways In scull.h line 47-49 #ifndef SCULL_MAJOR #define SCULL_MAJOR 0 /* dynamic major by default */ #endif

  27. load time : scull_load scull_major=10 By setting the major number an module parameter in main.c In line 615 of main.c int scull_init_module(void) : if (scull_major) { /*value of scull_major is assigned at scull.h to be 0 dev = MKDEV(scull_major, scull_minor); result = register_chrdev_region(dev, scull_nr_devs, "scull"); } else { result = alloc_chrdev_region(&dev, scull_minor, scull_nr_devs, "scull"); scull_major = MAJOR(dev); } The scull do it both ways In main.c 47-51 module_param(scull_major, int, S_IRUGO); module_param(scull_minor, int, S_IRUGO); module_param(scull_nr_devs, int, S_IRUGO); module_param(scull_quantum, int, S_IRUGO); module_param(scull_qset, int, S_IRUGO);

  28. Some Important Data Structures Most of the fundamental driver operations involve some important kernel data structures, called file_operations, file, and inode. cdev

  29. File Operations • The file_operations structure sets up operations • that applies to the reserved device numbers. • It is • defined in <linux/fs.h>, • a collection of function pointers. • 关联设备(号)及在其设备上的操作。这些操作主要用来实现系统调用,命名为open、read 等。

  30. Example from the text Each opened file is associated with its ownset of functions (byincluding a field calledf_op that points to a file_operations structure).

  31. File Operations

  32. File Operations The scull devicedriver implements only the mostimportant device operations. Its file_operations structure is as follows: Not all operations given in file have to be supported. struct file_operations scull_fops = { .owner = THIS_MODULE, .llseek = scull_llseek, .read = scull_read, .write = scull_write, .ioctl = scull_ioctl, .open = scull_open, .release = scull_release, };

  33. Difference between structure definition

  34. The cdev The functions hook the device’s file_operations to the cdev when adding the character device structure. 5 struct cdev { 6 struct kobject kobj; 7 struct module *owner; 8 struct file_operations *ops; 9 struct list_head list; 10 dev_t dev; 11 unsigned int count; 12 };

  35. The initialization of the module struct scull_qset { void **data; struct scull_qset *next; }; struct scull_dev { struct scull_qset *data; /* Pointer to first quantum set */ int quantum; /* the current quantum size */ int qset; /* the current array size */ unsigned long size; /* amount of data stored here */ unsigned int access_key; /* used by sculluid and scullpriv */ struct semaphore sem; /* mutual exclusion semaphore */ struct cdev cdev; /* Char device structure */ };

  36. The initialization of the module

  37. thank you !

More Related