User Tools

Site Tools


developersguide:driverprogramming

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revision Previous revision
Next revision
Previous revision
developersguide:driverprogramming [2015/03/05 17:50]
johnnoble [Getting Started] Changed hello pathname to be consistent with Minix 3.3
developersguide:driverprogramming [2022/02/10 14:58] (current)
stux add mknod /dev/time c 18 0
Line 10: Line 10:
 The following sections contain step-by-step instructions to try out programming simple device drivers. The following sections contain step-by-step instructions to try out programming simple device drivers.
  
-Please note that while this page may be slightly outdated, an up-to-date version of the hello driver is part of the MINIX3 source tree. You can find it in /​usr/​src/​minix/​drivers/​examples/​hello. The rest of this document will assume that the driver is not yet there, and will take you through the steps to create the driver yourself. Example 1 shows how to set up the infrastructure and create a very basic working driver process. Example 2 extends it into a simple character driver. The MINIX3 source tree's hello.c corresponds to the second example.+**Please note that while this page may be slightly outdated, an up-to-date version of the hello driver is part of the MINIX3 source tree.** You can find it in /​usr/​src/​minix/​drivers/​examples/​hello. The rest of this document will assume that the driver is not yet there, and will take you through the steps to create the driver yourself. Example 1 shows how to set up the infrastructure and create a very basic working driver process. Example 2 extends it into a simple character driver. The MINIX3 source tree's hello.c corresponds to the second example.
  
 ==== Example 1: Hello, World ==== ==== Example 1: Hello, World ====
Line 30: Line 30:
 PROG=   hello PROG=   hello
 SRCS=   ​hello.c SRCS=   ​hello.c
 +
 +FILES=${PROG}.conf
 +FILESNAME=${PROG}
 +FILESDIR= /​etc/​system.conf.d
  
 DPADD+= ${LIBCHARDRIVER} ${LIBSYS} DPADD+= ${LIBCHARDRIVER} ${LIBSYS}
Line 55: Line 59:
 } }
 </​code>​ </​code>​
-Ignore the sef_startup() call for now. We'll explain its purpose later. ​Now try to see if everything compiles correctly:+Ignore the sef_startup() call for now. We'll explain its purpose later.
  
-<code bash> +In addition, we must create a //hello.conf// file. It contains ​the permissions ​for the service. Each device driver typically only needs to access one real hardware device, and uses a few functions provided by Minix. To give our simple hello try-out driver enough permissions to experiment with, create ​//hello.conf// ​with:
-# make clean +
-# make +
-# make install +
-</​code>​ +
-If you did not receive any errors, it is time to try and run the device driver. Before we do that, we must modify the ///etc/system.conf// file. It contains ​a list of all system services (servers and drivers) and their permissions. Each device driver typically only needs to access one real hardware device, and uses a few functions provided by Minix. To give our simple hello try-out driver enough permissions to experiment with, append the following to ///etc/system.conf//:+
  
-///etc/system.conf//:+//hello.conf//:
  
 <​code>​ <​code>​
Line 81: Line 80:
 }; };
 </​code>​ </​code>​
-Starting, stopping and restarting device drivers must be done using the **service(8)** command. To start the hello program, enter the following command:+Now try to see if everything compiles correctly:​ 
 + 
 +<code bash> 
 +# make clean 
 +# make 
 +# make install 
 +</​code>​ 
 +If you did not receive any errors, it is time to try and run the device driver. ​Starting, stopping and restarting device drivers must be done using the **minix-service(8)** command. To start the hello program, enter the following command:
  
 <code bash> <code bash>
-# service up /​service/​hello+#minix-service up /​service/​hello
 Hello, World! Hello, World!
 RS: restarting /​service/​hello,​ restarts 0 RS: restarting /​service/​hello,​ restarts 0
 </​code>​ </​code>​
-And what a surprise, it displays the //Hello, World!// message :-) Stop the driver with:+And what a surprise, it displays the //Hello, World!// message :-) in the kernel log. Check with writing the contents of /​var/​log/​messages. ​Stop the driver with:
  
 <code bash> <code bash>
-# service down hello+#minix-service down hello
 </​code>​ </​code>​
 We received another message as well from the so-called //​Reincarnation Server// (RS). In Minix as a [[http://​en.wikipedia.org/​wiki/​Microkernel|microkernel]],​ device drivers are separate programs which send and receive message to communicate with the other operating system components. Device drivers, like any other program, may contain bugs and could crash at any point in time. The Reincarnation server will attempt to restart device drivers when it notices they are abruptly killed by the kernel due to a crash, or in our case when they exit(2) unexpectedly. You can see the Reincarnation Server in the process list as **rs**, if you use the **ps(1)** command. The Reincarnation Server sends keep-a-live messages to each running device driver on the system periodically,​ to ensure they are still responsible and not i.e. stuck in an infinite loop. We received another message as well from the so-called //​Reincarnation Server// (RS). In Minix as a [[http://​en.wikipedia.org/​wiki/​Microkernel|microkernel]],​ device drivers are separate programs which send and receive message to communicate with the other operating system components. Device drivers, like any other program, may contain bugs and could crash at any point in time. The Reincarnation server will attempt to restart device drivers when it notices they are abruptly killed by the kernel due to a crash, or in our case when they exit(2) unexpectedly. You can see the Reincarnation Server in the process list as **rs**, if you use the **ps(1)** command. The Reincarnation Server sends keep-a-live messages to each running device driver on the system periodically,​ to ensure they are still responsible and not i.e. stuck in an infinite loop.
Line 102: Line 108:
  
 ==== Example 2: /dev/hello ==== ==== Example 2: /dev/hello ====
-In this example we will extend the hello driver and re-implement it using //​libchardriver//​. Instead of just printing a hello on startup, we now want to use a device file ///​dev/​hello//​ to read the Hello World message. Each character and block driver is associated with a //major// device number. Thus, we need to pick a free major device number for the device--one that is not already in use for another device driver. We will use the major ID 17 in this example. We start by making the /dev/hello file:+In this example we will extend the hello driver and re-implement it using //​libchardriver//​. Instead of just printing a hello on startup, we now want to use a device file ///​dev/​hello//​ to read the Hello World message. Each character and block driver is associated with a //major// device number. Thus, we need to pick a free major device number for the device--one that is not already in use for another device driver. We will use the major ID 17 in this example, which is assigned to the hello driver as it is (see ''​minix/​dmap.h''​). We start by making the /dev/hello file:
  
 <code bash> <code bash>
Line 136: Line 142:
  
 /* /*
-  ​* Function prototypes for the hello driver. + * Function prototypes for the hello driver. 
-  */ + */ 
-static int hello_open(message *m); +static int hello_open(devminor_t minor, int access, endpoint_t user_endpt); 
-static int hello_close(message *m); +static int hello_close(devminor_t minor); 
-static ​struct device * hello_prepare(dev_t device); +static ​ssize_t hello_read(devminor_t minor, u64_t position, endpoint_t endpt
-static int hello_transfer(endpoint_t endpt, int opcode, u64_t position, +    ​cp_grant_id_t grantsize_t size, int flags, cdev_id_t id);
- iovec_t *iovunsigned int nr_reqendpoint_t user_endpt, unsigned ​int +
- flags);+
  
 /* SEF functions and variables. */ /* SEF functions and variables. */
 static void sef_local_startup(void);​ static void sef_local_startup(void);​
 static int sef_cb_init(int type, sef_init_info_t *info); static int sef_cb_init(int type, sef_init_info_t *info);
-static int sef_cb_lu_state_save(int);​+static int sef_cb_lu_state_save(int, int);
 static int lu_state_restore(void);​ static int lu_state_restore(void);​
  
Line 154: Line 158:
 static struct chardriver hello_tab = static struct chardriver hello_tab =
 { {
-    hello_open,​ +    ​.cdr_open = ​hello_open,​ 
-    hello_close,​ +    ​.cdr_close = ​hello_close,​ 
-    ​nop_ioctl, +    ​.cdr_read = hello_read,
-    hello_prepare,​ +
-    hello_transfer,​ +
-    nop_cleanup,​ +
-    nop_alarm,​ +
-    nop_cancel,​ +
-    nop_select,​ +
-    NULL+
 }; };
  
-/** Represents the /dev/hello device. */ +/** State variable to count the number of times the device has been opened. 
-static struct device hello_device;​ + * Note that this is not the regular type of open counter: it never decreases. 
- + */
-/** State variable to count the number of times the device has been opened. */+
 static int open_counter;​ static int open_counter;​
  
-static int hello_open(message *UNUSED(m))+static int hello_open(devminor_t ​UNUSED(minor), int UNUSED(access),​ 
 +    endpoint_t UNUSED(user_endpt))
 { {
     printf("​hello_open(). Called %d time(s).\n",​ ++open_counter);​     printf("​hello_open(). Called %d time(s).\n",​ ++open_counter);​
Line 178: Line 175:
 } }
  
-static int hello_close(message *UNUSED(m))+static int hello_close(devminor_t ​UNUSED(minor))
 { {
     printf("​hello_close()\n"​);​     printf("​hello_close()\n"​);​
Line 184: Line 181:
 } }
  
-static ​struct device * hello_prepare(dev_t UNUSED(dev))+static ​ssize_t hello_read(devminor_t ​UNUSED(minor), u64_t position, 
 +    endpoint_t endpt, cp_grant_id_t grant, size_t size, int UNUSED(flags),​ 
 +    cdev_id_t UNUSED(id))
 { {
-    ​hello_device.dv_base = make64(0, 0)+    ​u64_t dev_size
-    ​hello_device.dv_size = make64(strlen(HELLO_MESSAGE),​ 0)+    ​char *ptr
-    ​return &​hello_device+    ​int ret
-}+    char *buf = HELLO_MESSAGE;​
  
-static int hello_transfer(endpoint_t endpt, int opcode, u64_t position, +    printf("​hello_read()\n");
-    iovec_t *iov, unsigned nr_req, endpoint_t UNUSED(user_endpt),​ +
-    unsigned int UNUSED(flags)) +
-+
-    int bytes, ret;+
  
-    ​printf("​hello_transfer()\n");+    ​/* This is the total size of our device. */ 
 +    dev_size = (u64_tstrlen(buf);
  
-    ​if (nr_req != 1) +    /* Check for EOF, and possibly limit the read size. */ 
-    { +    ​if ​(position >= dev_sizereturn 0; /* EOF */ 
-        ​/* This should never trigger ​for character drivers at the moment. */ +    ​if (position + size > dev_size) 
-        ​printf("​HELLO:​ vectored transfer request, using first element only\n"​); +        size = (size_t)(dev_size - position);​ /​* limit size */
-    ​}+
  
-    ​bytes strlen(HELLO_MESSAGE- ex64lo(position) < iov->​iov_size ? +    ​/* Copy the requested part to the caller. */ 
-            ​strlen(HELLO_MESSAGE) - ex64lo(position: iov->​iov_size;+    ptr buf + (size_t)position; 
 +    ​if ​((ret = sys_safecopyto(endpt,​ grant, 0, (vir_bytesptr, size)) != OK) 
 +        return ret;
  
-    ​if (bytes <= 0) +    ​/* Return the number of bytes read. */ 
-    { +    return ​size;
-        return OK; +
-    } +
-    switch (opcode) +
-    { +
-        case DEV_GATHER_S:​ +
-            ret = sys_safecopyto(endpt,​ (cp_grant_id_t) iov->​iov_addr,​ 0, +
-                                (vir_bytes) (HELLO_MESSAGE + ex64lo(position)),​ +
-                                 ​bytes);​ +
-            iov->​iov_size -= bytes; +
-            break; +
- +
-        default: +
-            return EINVAL; +
-    } +
-    return ​ret;+
 } }
  
-static int sef_cb_lu_state_save(int UNUSED(state)) {+static int sef_cb_lu_state_save(int UNUSED(state), int UNUSED(flags)) {
 /* Save the state. */ /* Save the state. */
     ds_publish_u32("​open_counter",​ open_counter,​ DSF_OVERWRITE);​     ds_publish_u32("​open_counter",​ open_counter,​ DSF_OVERWRITE);​
Line 248: Line 230:
 { {
     /*     /*
-          ​* Register init callbacks. Use the same function for all event types +     * Register init callbacks. Use the same function for all event types 
-          */+     ​*/
     sef_setcb_init_fresh(sef_cb_init);​     sef_setcb_init_fresh(sef_cb_init);​
     sef_setcb_init_lu(sef_cb_init);​     sef_setcb_init_lu(sef_cb_init);​
Line 255: Line 237:
  
     /*     /*
-          ​* Register live update callbacks. +     * Register live update callbacks. 
-          */ +     ​*/
-    /* - Agree to update immediately when LU is requested in a valid state. */ +
-    sef_setcb_lu_prepare(sef_cb_lu_prepare_always_ready);​ +
-    /* - Support live update starting from any standard state. */ +
-    sef_setcb_lu_state_isvalid(sef_cb_lu_state_isvalid_standard);​ +
-    /* - Register a custom routine to save the state. ​*/+
     sef_setcb_lu_state_save(sef_cb_lu_state_save);​     sef_setcb_lu_state_save(sef_cb_lu_state_save);​
  
Line 304: Line 281:
 { {
     /*     /*
-          ​* Perform initialization. +     * Perform initialization. 
-          */+     ​*/
     sef_local_startup();​     sef_local_startup();​
  
     /*     /*
-          ​* Run the main loop. +     * Run the main loop. 
-          */ +     ​*/ 
-    chardriver_task(&​hello_tab, CHARDRIVER_SYNC);+    chardriver_task(&​hello_tab);​
     return OK;     return OK;
 } }
 </​code>​ </​code>​
-Let's try to understand what the above code does. First, it has several #include lines for the required prototypes and functions used in the program. Then, it declares a //struct chardriver//​. This structure is filled with [[http://​en.wikipedia.org/​wiki/​Callback_(computer_science)|callback]] functions which will be invoked by //​libchardriver//​ at runtime. It has callback functions for performing open, read, write and close operations on the device driver. Most of the action happens in the //hello_transfer// function. It used the //​sys_safecopyto//​ function to copy the HELLO_MESSAGE string from the device driver program, to the user program reading from /dev/hello. The operating system will then on behalf of the device driver take care of properly copying the bytes between the two programs. An Input/​Output vector //iov// is used by the //​sys_safecopyto//​ function to describe the memory address for storing bytes and number of requested bytes by the user application.+Let's try to understand what the above code does. First, it has several #include lines for the required prototypes and functions used in the program. Then, it declares a //struct chardriver//​. This structure is filled with [[http://​en.wikipedia.org/​wiki/​Callback_(computer_science)|callback]] functions which will be invoked by //​libchardriver//​ at runtime. It has callback functions for performing open, read, write and close operations on the device driver. Most of the action happens in the //hello_read// function. It used the //​sys_safecopyto//​ function to copy the HELLO_MESSAGE string from the device driver program, to the user program reading from /dev/hello. The operating system will then on behalf of the device driver take care of properly copying the bytes between the two programs. An Input/​Output vector //iov// is used by the //​sys_safecopyto//​ function to describe the memory address for storing bytes and number of requested bytes by the user application.
  
 In the //main()// function there are only two simple calls. //​sef_local_startup()//​ is called at the very beginning to register [[.:​sef|SEF]] callbacks and then complete initialization. In //​sef_local_startup()//​ the same function //​sef_cb_init_fresh()//​ is registered as a callback for all the supported initialization types. This means that the initialization code executed when the driver starts will be always the same regardless of the device driver starting fresh, after a live update, or after a restart. In the //main()// function there are only two simple calls. //​sef_local_startup()//​ is called at the very beginning to register [[.:​sef|SEF]] callbacks and then complete initialization. In //​sef_local_startup()//​ the same function //​sef_cb_init_fresh()//​ is registered as a callback for all the supported initialization types. This means that the initialization code executed when the driver starts will be always the same regardless of the device driver starting fresh, after a live update, or after a restart.
Line 325: Line 302:
 Finally, the //main()// function executes //​chardriver_task()//​ to let //​libchardriver//​ start processing user requests and invoke our driver callback functions. Finally, the //main()// function executes //​chardriver_task()//​ to let //​libchardriver//​ start processing user requests and invoke our driver callback functions.
  
-Now compile the program again using the same commands as in example 1, and start the driver with the **service(8)** command. We supply ''​-dev /​dev/​hello''​ to indicate that the newly started driver is responsible for the major device identified by /dev/hello - namely, major 17, which we picked earlier.+Now compile the program again using the same commands as in example 1, and start the driver with the **minix-service(8)** command. We supply ''​-dev /​dev/​hello''​ to indicate that the newly started driver is responsible for the major device identified by /dev/hello - namely, major 17, which we picked earlier.
  
 <code bash> <code bash>
-# service up /usr/sbin/hello -dev /dev/hello+minix-service up /service/hello -dev /dev/hello
 Hello, World! Hello, World!
 </​code>​ </​code>​
Line 338: Line 315:
 # cat /dev/hello # cat /dev/hello
 hello_open() hello_open()
-hello_transfer() +hello_read() 
-hello_transfer()+hello_read()
 hello_close() hello_close()
 Hello, World! Hello, World!
Line 346: Line 323:
 If you get the message above, the hello driver works!** :D ** If you get the message above, the hello driver works!** :D **
  
-Now let's try to restart the driver with the **service(8)** command to simulate a failure:+Now let's try to restart the driver with the **minix-service(8)** command to simulate a failure:
  
 <code bash> <code bash>
-# service refresh hello+minix-service refresh hello
 Hello, World! Hello, World!
 Hey, I've just been restarted! Hey, I've just been restarted!
 # cat /dev/hello # cat /dev/hello
 hello_open() hello_open()
-hello_transfer() +hello_read() 
-hello_transfer()+hello_read()
 hello_close() hello_close()
 Hello, World! Hello, World!
Line 373: Line 350:
 #endif /* __HELLO_H */ #endif /* __HELLO_H */
 </​code>​ </​code>​
-Now recompile it and update the driver with the **service(8)** command:+Now recompile it and update the driver with the **minix-service(8)** command:
  
 <code bash> <code bash>
Line 379: Line 356:
 # make # make
 # make install # make install
-# service update /usr/sbin/hello -state 1    # request an update state where no work is in progress (i.e. SEF_LU_STATE_WORK_FREE=1)+minix-service update /service/hello -state 1    # request an update state where no work is in progress (i.e. SEF_LU_STATE_WORK_FREE=1)
 Hello, New World! Hello, New World!
 Hey, I'm a new version! Hey, I'm a new version!
 # cat /dev/hello # cat /dev/hello
 hello_open() hello_open()
-hello_transfer() +hello_read() 
-hello_transfer()+hello_read()
 hello_close() hello_close()
 Hello, New World! Hello, New World!
Line 464: Line 441:
 struct chardriver time_tab = struct chardriver time_tab =
 { {
-        ​*cdr_open = time_open,​ +        ​.cdr_open = time_open,​ 
-        ​*cdr_close = time_close,​ +        ​.cdr_close = time_close,​ 
-        ​*cdr_read = time_read,+        ​.cdr_read = time_read,
 }; };
  
Line 604: Line 581:
 SRCS=   ​time.c SRCS=   ​time.c
  
-FILES=$(PROG).conf +FILES=${PROG}.conf 
-FILESNAME=$(PROG)+FILESNAME=${PROG}
 FILESDIR=/​etc/​system.conf.d FILESDIR=/​etc/​system.conf.d
  
 DPADD+= ${LIBCHARDRIVER} ${LIBSYS} DPADD+= ${LIBCHARDRIVER} ${LIBSYS}
 LDADD+= -lchardriver -lsys LDADD+= -lchardriver -lsys
- 
- 
  
 .include <​minix.service.mk>​ .include <​minix.service.mk>​
 </​code>​ </​code>​
  
-Now we need to give the new time device driver access to the CMOS ports 0x70 and 0x71 using the time.conf file mentioned in the Makefile. Putting it in /​etc/​system.d/​ lets service read it. This is the contents:+Now we need to give the new time device driver access to the CMOS ports 0x70 and 0x71 using the //time.conf// file mentioned in the Makefile. Putting it in /​etc/​system.d/​ lets service read it. This is the contents:
  
 <​code>​ <​code>​
Line 648: Line 623:
     install ​ /​service/​time     install ​ /​service/​time
     install ​ /​etc/​system.conf.d/​time     install ​ /​etc/​system.conf.d/​time
-# service up /​service/​time -dev /dev/time+mknod /dev/time c 18 0 
 +# minix-service up /​service/​time -dev /dev/time
 # cat /​dev/​time ​ # cat /​dev/​time ​
 2014-09-10 15:48:21 2014-09-10 15:48:21
developersguide/driverprogramming.1425574251.txt.gz · Last modified: 2015/10/26 12:39 (external edit)