Character Device Drivers One Module - Multiple Devices

Similar documents
Linux Device Drivers

The Note/1 Driver Design

Character Device Drivers

REVISION HISTORY NUMBER DATE DESCRIPTION NAME

Concurrency and Race Conditions (Linux Device Drivers, 3rd Edition (

CS 378 (Spring 2003)

Linux Device Drivers. 3. Char Drivers cont. 3. Char Drivers. 1. Introduction 2. Kernel Modules 3. Char Drivers 4. Advanced Char Drivers 5.

What is a Linux Device Driver? Kevin Dankwardt, Ph.D. VP Technology Open Source Careers

Linux Device Drivers. 3. Char Drivers. 1. Introduction 2. Kernel Modules 3. Char Drivers 4. Advanced Char Drivers 5. Interrupts

Introduction Reading Writing scull. Linux Device Drivers - char driver

Introduction to Operating Systems. Device Drivers. John Franco. Dept. of Electrical Engineering and Computing Systems University of Cincinnati

1 #include <linux/module.h> 2 #include <linux/sched.h> 3 #include <linux/interrupt.h> 4 #include <linux/init.h> 5 #include <linux/kernel.

Kernel Modules. Kartik Gopalan

Designing and developing device drivers. Coding drivers

NPTEL Course Jan K. Gopinath Indian Institute of Science

Introduction to Linux Device Drivers Recreating Life One Driver At a Time

USB. Development of a USB device driver working on Linux and Control Interface. Takeshi Fukutani, Shoji Kodani and Tomokazu Takahashi

Linux drivers - Exercise

CS 423 Operating System Design: Introduction to Linux Kernel Programming (MP1 Q&A)

Device Drivers Demystified ESC 117. Doug Abbott, Principal Consultant Intellimetrix. Why device drivers? What s a device driver?

Memory Mapping. Sarah Diesburg COP5641

The device driver (DD) implements these user functions, which translate system calls into device-specific operations that act on real hardware

Interrupt handling. Interrupt handling. Deferred work. Interrupt handling. Remove an interrupt handler. 1. Link a struct work to a function

NPTEL Course Jan K. Gopinath Indian Institute of Science

Embedded data structures and lifetime management

Prof. Dr. Hasan Hüseyin

Linux Device Drivers Interrupt Requests

MP3: VIRTUAL MEMORY PAGE FAULT MEASUREMENT

Kernel Internals. Course Duration: 5 days. Pre-Requisites : Course Objective: Course Outline

Simple char driver. for Linux. my_first.c: headers. my_first.c: file structure. Calcolatori Elettronici e Sistemi Operativi.

Finish up OS topics Group plans

Memory Mapped I/O. Michael Jantz. Prasad Kulkarni. EECS 678 Memory Mapped I/O Lab 1

CS5460/6460: Operating Systems. Lecture 24: Device drivers. Anton Burtsev April, 2014

Memory Management: The process by which memory is shared, allocated, and released. Not applicable to cache memory.

Hunting Down Data Races in the Linux Kernel

Formal Verification of Linux Device Drivers

Administrivia. Introduction to Computer Systems. Pointers, cont. Pointer example, again POINTERS. Project 2 posted, due October 6

7.4 Simple example of Linux drivers

Loadable Kernel Module

Final Step #7. Memory mapping For Sunday 15/05 23h59

CSC369 Lecture 2. Larry Zhang, September 21, 2015

Introduction to C: Pointers

- Knowledge of basic computer architecture and organization, ECE 445

Do not start the test until instructed to do so!

Class Information ANNOUCEMENTS

Lecture. DM510 - Operating Systems, Weekly Notes, Week 11/12, 2018

POSIX Shared Memory. Linux/UNIX IPC Programming. Outline. Michael Kerrisk, man7.org c 2017 November 2017

Short Notes of CS201

Programming the GPMC driver

CS201 - Introduction to Programming Glossary By

Computer Core Practice1: Operating System Week10. locking Protocol & Atomic Operation in Linux

Concurrency Aspects of Project 2

Linux Synchronization Mechanisms in Driver Development. Dr. B. Thangaraju & S. Parimala

THREADS: (abstract CPUs)

REVISION HISTORY NUMBER DATE DESCRIPTION NAME

Synchronization Spinlocks - Semaphores

Project 2-1 User Programs

CSC209: Software tools. Unix files and directories permissions utilities/commands Shell programming quoting wild cards files

CSC209: Software tools. Unix files and directories permissions utilities/commands Shell programming quoting wild cards files. Compiler vs.

Linux DRM Developer s Guide

CSC369 Lecture 2. Larry Zhang

Virtual File System (VFS) Implementation in Linux. Tushar B. Kute,

Step Motor. Step Motor Device Driver. Step Motor. Step Motor (2) Step Motor. Step Motor. source. open loop,

Concurrency, Thread. Dongkun Shin, SKKU

Operating Systems, Assignment 2 Threads and Synchronization

Recitation 14: Proxy Lab Part 2

Synchronization. CS61, Lecture 18. Prof. Stephen Chong November 3, 2011

Motivation was to facilitate development of systems software, especially OS development.

Unix (Linux) Device Drivers

Crit-bit Trees. Adam Langley (Version )

Synchronization I. Jo, Heeseung

Unleashing D* on Android Kernel Drivers. Aravind Machiry

Embedded Systems Programming

RCU in the Linux Kernel: One Decade Later

ENCM 501 Winter 2019 Assignment 9

Linux Kernel Application Interface

Operating systems for embedded systems

Chapter 6: Process Synchronization

Here's how you declare a function that returns a pointer to a character:

Laboratory Assignment #3. Extending scull, a char pseudo-device

Parameter passing. Programming in C. Important. Parameter passing... C implements call-by-value parameter passing. UVic SEng 265

CSci 4061 Introduction to Operating Systems. Programs in C/Unix

CPSC 8220 FINAL EXAM, SPRING 2016

Table of Contents. Preface... xi

CSC209 Review. Yeah! We made it!

CSE 451: Operating Systems Winter Lecture 7 Synchronization. Steve Gribble. Synchronization. Threads cooperate in multithreaded programs

CS 453: Operating Systems Programming Project 5 (100 points) Linux Device Driver

Data Storage. August 9, Indiana University. Geoffrey Brown, Bryce Himebaugh 2015 August 9, / 19

Embedded Linux. Session 4: Introduction to the GPIO Kernel API. Martin Aebersold BFH-TI Dipl. El.-Ing. FH.

A Crash Course in C. Steven Reeves

MaRTE OS Misc utilities

CS140 Operating Systems Final December 12, 2007 OPEN BOOK, OPEN NOTES

CSE 509: Computer Security

CS342 - Spring 2019 Project #3 Synchronization and Deadlocks

SYSTEM CALL IMPLEMENTATION. CS124 Operating Systems Fall , Lecture 14

Exercise Session 2 Systems Programming and Computer Architecture

CS 153 Design of Operating Systems Winter 2016

2 nd Half. Memory management Disk management Network and Security Virtual machine

Pointers. 1 Background. 1.1 Variables and Memory. 1.2 Motivating Pointers Massachusetts Institute of Technology

Driving Me Nuts Writing a Simple USB Driver

Transcription:

Review from previous classes Three Types: Block, Character, and Network Interface Device Drivers MAJOR & MINOR numbers assigned register_chrdev_region(), alloc_chrdev_region(), unregister_chrdev_region() Examined Install and Cleanup Scripts struct file_operations, struct cdev, struct inode, and struct file open(), read(), write(), and release() Kernel Primitives for Memory Access: copy_to_user(), copy_from_user(), put_user(), and get_user(). request_region(), release_region() outb(), inb() ndelay(), udelay(), mdelay() 2010-10-20 1

One Module for Multiple Devices App Driver DEV 1 DEV 2 The module will register multiple minor device numbers, one for each hardware device it plans to control. Consider COM ports. One common driver for all COM ports. This concept leads to critical region management. What s a critical region of code? 2010-10-20 2

Case Study: Stepper Motor via Parallel Port App L R Driver? Motor 0 Motor 1 Out of the parallel port (0x378) bit 7 6 5 4 3 2 1 0 winding D C B A D C B A motor ------------- ------------- motor 1 motor 0 Rotate Pattern: A AB B BC C CD D DA and repeat 2010-10-20 3

Case Study: Stepper Motor via Parallel Port Our general algorithm for write() would be static int pos = 0; static char motor_table[8] = { 0x01, 0x03, 0x02, 0x06, 0x04, 0x0C, 0x08, 0x09 }; int module4_write (struct file *fp, const char user *buffer, size_t len, loff_t *offset) { int bytes_written = 0; char ch; const char *tmp; printk (KERN_INFO "module4: module4_write... \n"); tmp = buffer + len - 1; copy_from_user (&ch, tmp, 1); bytes_written = 1; 2010-10-20 4

Case Study: Stepper Motor via Parallel Port switch (ch) { case 'L': pos++; if (pos > 7) pos = 0; break; case 'R': } pos--; if (pos < 0) pos = 7; break; ch = motor_table[pos]; outb (ch, MODULE4_PORT); module4_lastbytewritten = ch; return bytes_written; } /* end module4_write */ 2010-10-20 5

Case Study: Stepper Motor via Parallel Port static char motor_table[8] = { 0x01, 0x03, 0x02, 0x06, 0x04, 0x0C, 0x08, 0x09 }; Bit pattern is only for motor0 Same pattern can be used for motor1 Can we use one module to control both motors? What do we need to keep track of? 2010-10-20 6

Case Study: Stepper Motor via Parallel Port We need to treat the parallel port data as two separate instances of data (low 4 bits for one motor, high 4 bits for the second) We need to extend our module, so that we can create two nodes within the /dev folder - one for motor0 and one for motor1 We have already seen how to allocate and register a cdev structure for an existing node we wish to install in /dev. Now we allocate two minor numbers, and register two cdev structures for, one for each minor number Let s examine the initialization process 2010-10-20 7

Case Study: Stepper Motor via Parallel Port // we now need two separate cdev structures for the two nodes static int motor0_cdev; static int motor1_cdev; static struct semaphore motor_semaphore; // discussed later! static int init initialize_motor(void) { int rc; printk(kern_info "motor: initialize_motor() called.\n"); // allocate a major number for our module, along with 2 // minor numbers this time! "motor_minor" is our first of // two minor numbers. Thus, we'll have minor numbers // 0 and 1 for this driver. >> rc = alloc_chrdev_region (&motor_devicenumber, motor_minor, 2, motor_name); if (rc < 0) { printk(kern_info "motor: unable to allocate device region.\n"); goto init_error_0; } /* endif */ 2010-10-20 8

Case Study: Stepper Motor via Parallel Port motor_major = MAJOR (motor_devicenumber); printk (KERN_INFO "motor: during INIT... major: %d minor: %d\n", motor_major, motor_minor); // initialize and register our character driver's file operations // we'll do this TWICE... once for minor 0, once for minor 1 >> cdev_init (&motor0_cdev, &motor_fops); motor0_cdev.owner = THIS_MODULE; motor0_cdev.ops = &motor_fops; >> rc = cdev_add (&motor0_cdev, MKDEV(motor_major, motor_minor), 1); if (rc) { printk(kern_info "motor: unable to add cdev struct.\n"); goto init_error_1; } /* endif */ 2010-10-20 9

Case Study: Stepper Motor via Parallel Port >> cdev_init (&motor1_cdev, &motor_fops); motor1_cdev.owner = THIS_MODULE; motor1_cdev.ops = &motor_fops; >> rc = cdev_add (&motor1_cdev, MKDEV(motor_major, motor_minor + 1), 1); if (rc) { printk(kern_info "motor: unable to add cdev struct.\n"); goto init_error_2; } /* endif */ 2010-10-20 10

Case Study: Stepper Motor via Parallel Port // now, initialize our access to I/O Port hardware // for this example, we're going to try to connect to // parallel port. Note that even though we have two // "logical" motors to support, there's only ONE parallel // port that they're controlled with! motor_region = request_region (MOTOR_PORT, 1, motor_name); if (motor_region == NULL) { printk (KERN_INFO "motor: unable to gain exclusive access to parallel port... " "but we'll use it anyways!\n"); // uncomment the next 2 lines if you wish to exit with // an error condition // rc = -EIO; // goto init_error_3; } /* endif */ 2010-10-20 11

Case Study: Stepper Motor via Parallel Port // initialize semaphore to control critical regions init_mutex (&motor_semaphore); // discussed later printk (KERN_INFO "motor: successfully completed init!\n"); return 0; // common error support for initialization init_error_3: cdev_del (&motor1_cdev); init_error_2: cdev_del (&motor0_cdev); init_error_1: unregister_chrdev_region (motor_devicenumber, 2); init_error_0: return rc; } /* end initialize_motor */ 2010-10-20 12

Two Motors with Common Handler Now let s look at the WRITE process again Select the appropriate value out of the table, and write the value to the parallel port. However, the work for motor1 is slightly different We need to shift the motor table value up by 4 bits, and then feed it out the parallel port. To avoid altering the positional information for motor0, We need to OR the data for motor1 with the existing data for motor0. And of course, vice versa if we're controlling motor0 2010-10-20 13

Two Motors with Common Handler int motor_write (struct file *fp, const char user *buffer, size_t len, loff_t *offset) We're missing a crucial piece of information - the inode structure We can't dereference this data during a write() operation, and hence, it appears we're destined to implement two separate drivers for the two motors. So, what do we do? 2010-10-20 14

Two Motors with Common Handler int motor_open (struct inode *i, struct file *fp) The solution is within the file structure during the OPEN The file structure has a member called private_data, and is usually set to NULL to indicate "no private data". We allocate our own relative information to this pointer and we ll have access to it during the write process. 2010-10-20 15

Two Motors with Common Handler #include <asm/semaphore.h> typedef struct MOTOR_DATA { int pos; int minor; struct semaphore *sem; } MOTOR_DATA; // discussed later We dynamically allocate one of these structures extract the minor number from the user's request to open a node initialize the positional data member of this structure, connect the address of this structure to the private data member of the file structure passed down to the write() support (as well as read() and other functions) In release(), we deallocate the structure. 2010-10-20 16

Two Motors with Common Handler As usual, the KERNEL has versions of memory allocation and deallocation. kmalloc() for allocation of memory kfree() for release of memory 2010-10-20 17

Two Motors with Common Handler // note: two flags, one for each motor, to ensure that only // one top level app has a given motor open at one time static int motor0_flagopen = 0; static int motor1_flagopen = 0; int motor_open (struct inode *i, struct file *fp) { int minor; MOTOR_DATA *pmotordata; // dereference device number from inode structure, // and extract the minor number. There are two macros, // imajor() and iminor(), which are used to extract the // major and minor values out of an inode structure! >> minor = iminor (i); printk (KERN_INFO "motor: motor_open for %d starting... \n", minor); 2010-10-20 18

Two Motors with Common Handler // we will limit out char driver to only allow one // // Hence, return an error code (EBUSY in this case) >> switch (minor) { case 0: if (motor0_flagopen) { return -EBUSY; } /* endif */ motor0_flagopen++; break; case 1: if (motor1_flagopen) { return -EBUSY; } /* endif */ motor1_flagopen++; break; default: // a fault... invalid minor device number! return -EFAULT; } /* end switch */ 2010-10-20 19

Two Motors with Common Handler // at this point, either motor0 or motor1 has been // opened, and is now in use once and only once. // now we can allocate private data for this request // and store away in the file structure for later use. // NOTE: you MUST use kmalloc() and kfree() to support // kernel level memory allocation. The normal user level // malloc() and free() cannot be used! >> pmotordata = (MOTOR_DATA *)kmalloc (sizeof (MOTOR_DATA), GFP_KERNEL); if (pmotordata == NULL) { return -EFAULT; } /* endif */ // init our private data appropriately, and store the // pointer in our file structure pmotordata->minor = minor; pmotordata->pos = 0; pmotordata->sem = &motor_semaphore; // discussed later >> fp->private_data = (void *)pmotordata; 2010-10-20 20

Update to the write() Support Now we have a mechanism for accessing the specific motor How do we use it? 2010-10-20 21

Update to the write() Support int motor_write (struct file *fp, const char user *buffer, size_t len, loff_t *offset) { int bytes_written = 0; char ch; const char *tmp; MOTOR_DATA *pmotordata; // fetch our private motor data structure >> pmotordata = (MOTOR_DATA *)fp->private_data; printk (KERN_INFO "motor: motor_write %d... \n", pmotordata->minor); 2010-10-20 22

Update to the write() Support tmp = buffer + len - 1; copy_from_user (&ch, tmp, 1); bytes_written = 1; >> switch (ch) { case 'L': pmotordata->pos++; if (pmotordata->pos > 7) pmotordata->pos = 0; break; case 'R': } pmotordata->pos--; if (pmotordata->pos < 0) pmotordata->pos = 7; break; >> ch = motor_table[pmotordata->pos]; 2010-10-20 23

Update to the write() Support // ensure we combine the motor table data found above, // // mask appropriately. // AAA >> switch (pmotordata->minor) { case 0: motor_lastbytewritten = motor_lastbytewritten & 0xF0; motor_lastbytewritten = motor_lastbytewritten ch; break; case 1: motor_lastbytewritten = motor_lastbytewritten & 0x0F; motor_lastbytewritten = motor_lastbytewritten (ch << 4); break; } /* end switch */ >> outb (motor_lastbytewritten, MOTOR_PORT); // BBB return bytes_written; } /* end motor_write */ 2010-10-20 24

Concurrency A module may in fact be rescheduled to run at a later time, if the Linux kernel deems something more worthy needs to go first. When we're altering the value in our global variable motor_lastbytewritten, and since we can have two opened motors concurrently, there exists a race condition during this adjustment. So, what do we do? 2010-10-20 25

Concurrency We solve this problem with either a semaphore or a mutex Within the write() code (between AAA and BBB) we have our CRITICAL REGION semaphore needs to be initialized static struct semaphore motor_semaphore;... // inside the initialization function... init_mutex (&motor_semaphore); 2010-10-20 26

Concurrency After initialization, we add the reference to the private_data structure // init our private data appropriately, and store the // pointer in our file structure pmotordata->minor = minor; pmotordata->pos = 0; >> pmotordata->sem = &motor_semaphore; fp->private_data = (void *)pmotordata; 2010-10-20 27

Concurrency To use the mutex within the write() /* * insert in place of // AAA */ >> if (down_interruptible (pmotordata->sem)) return -ERESTARTSYS; /* * insert in place of // BBB */ >> up (pmotordata->sem); 2010-10-20 28

Concurrency The down_interruptible() function manipulates the kernel semaphore cause a module to wait until the semaphore becomes available. This is essentially using the semaphore as a mutex object (supporting our required mutual exclusion). To release a semaphore, use the companion up() function. For more info: 1. Do a Google search for down_interruptible() to find out other ways to set up a mutex! 2. LDD #3 Chapter 5 Definitely take a look at Completions, SpinLocks, and seqlocks 2010-10-20 29

In-Class Exercises Notes for Week 6 Student exercise to complete the CLEAR and RELEASE parts of today s class Exercise: Work on Assignment #3 Instead: Review Assignment #3 in class and discuss how to implement based upon the information reviewed in class. ioctl() will be reviewed next class. Need to know Project Choice by everyone by end of class. 2010-10-20 30

For next class Read the notes for Weeks 1 to 6 Read Linux Device Drivers Chapters 1, 2, 3, 5, & 9 Reminders: Mid-Term in 2 weeks October 28 th Projects Due: December 9 th 2010-10-20 31