You are on page 1of 42

EE4214 REAL TIME EMBEDDED SYSTEMS

Group 9 Clinic Queue Management System


PROJECT REPORT
DATED : 13 November 2009

Cheung Zi Eu Augustine - U070394R


Huynh Le Trung - U066027B
Mateja Dokleja - NT090887R
Ritika Taragi - U066042L
Sahil Gupta - U066618E
Tiew Ee Yeong - U070447U

CONTENTS
1. PROJECT INTRODUCTION.......................................................................................................4
1.1

Overview .................................................................................................................4

1.2

Detailed Description ................................................................................................5

1.2.1 Data collection mechanism.......................................................................................5


1.2.1.1 Graphical User Interface...........................................................................5
1.2.1.2 mySQL database......................................................................................7
1.2.1.3 Input using Bluetooth technology.............................................................7
1.2.2 Data Transfer through Socket Mechanism using TCP/IP.........................................7
1.2.3 Room Allocation Mechanism....................................................................................8
1.2.4 Process Scheduling/ Task handling..........................................................................9
1.2.5 Speaker...................................................................................................................10
1.2.6 LEDs...........................................................................................10
1.2.7 Push Button and Interrupts ......10
1.2.8 Clinic System Display 10
1.2.9 Patient Threshold ..11
1.3 System Manual (user level) .................11
1.4

System Flowchart (High level overview) ...............................................................12

1.5

Use Case Diagram.................................................................................................12

2. Software Design Features .......................................................................................................13


2.1 I/O facilities......................................................................13
2.2 Process Scheduling...........................................................................................................13
2.3 & Inter-process communication.........................................................................................13
2.4 Deadlock Management.....................................................................................................14

2.5 Mutual Exclusion...............................................................................................................14


3. Software Design Diagram..........................................................................................................15
3.1 Data Flow Diagram.....................................................................................................15
3.1.1 Level 0........................................................................................................15
3.1.2 Level 1........................................................................................................15
4. Testing......................................................................................................................................16
5. Learning Outcomes...................................................................................................................17

APPENDIX
Project Code.................................................................................................................................21

1 PROJECT INTRODUCTION
1.1 OVERVIEW
Hospitals and clinics provide essential and mandatory health care services to the people. With
the growing volume of patients seeking medical attention everyday, it is imperative to have in
place a smooth functioning Service Management System to ensure highest standards of
serviceability. An inefficient system could lead to overcrowding of clinics, a strain on facilities, and
inconvenience to patients, making health care more inaccessible and inequitable for the
population.
For our project, we propose to develop a fair Clinic Queue Management System to allocate
patients to a well managed hospital Queue Management System (QMS).

1.2 DETAILED DESCRIPTION


The aim of the project is to build a Real Time Queue Management System. The system manages
the following activities:
-

Data collection from Incoming patients using a friendly Graphical User Interface (GUI)

Data transfer from GUI (workstation) to Queue Management module (uCsimm board)

Room allocation based on a planned algorithm

Room occupancy status display through the board LEDs

Speaker to indicate incoming patient.

In this section, we will describe the Queue Management System, in detailed steps.
1.2.1. Data collection mechanism
Data collection mechanism is divided into 2 main parts: Graphic User Interface(GUI) for
interaction with user and mySQL database for retrieving users data and medical history.
1.2.1.1. Graphic User Interface (GUI)
GUI is user friendly interface that allows patient to log in using his unique user identification
number. When that happens, first GUI connect to mySQL database and searches for patients ID.
If ID is found, patients information and medical history are retrieved from database presented in
box Patient data. After that, random values generator mechanism is used to simulate results of
patients initial examination. For key factors 0 or 1 is randomly generated and that information is
presented in Initial examination results box.

Data retrieved from the database and randomly generated data is chosen in the way to fit chosen
prediction model. Prediction model which was used was developed originally for pneumonia by
the team of researchers supported through a grant from the Federal Agency for Health Care
Policy and Research and has been validated as being accurate and applicable. Model goes
though all the key factors and for each of them, according to its status, certain value is added up
to patients sum. After that, patient is put into one of 5 risk categories according to his/her sum. As
our system supports only normal and emergency patients, patients of upper two categories, of
medium and high risk, are considered emergency patients while everybody else is considered
normal patients.
For patients to be sent to the board and to the queue, application has to be connected to the
board. That is done by clicking on the Connect with hospital button. When button is pressed,
socket is created on fixed IP address 168.192.1.1. and port 4242 and GUI acts like a server and

listens on the port of the attempt of connecting by the client. When the connection is established,
patients can be sent into the queue by sending letter e or n over the Ethernet using TCP/IP
protocol, according to patients calculated status. Both sending and establishing the connection
are done in by using asynchronous functions which do their job and then when other side (board
as a client) responds callback function is evoked to process that response. That way the program
is not blocked while waiting for action of other side and its possible to execute other tasks. On
the very bottom of the screen, at status strip information about connection are displayed.
Additionally, GUI has a log box where information about events relevant to the user are displayed.
1.2.1.2. mySQL database
MySQL database is used to store information about patients details and past medical records.
GUI connects to the database when ID is provided and gets that information. Database consists
only of one table, Patients, and is protected by password. In the table patients id is used as a
primary key of the database so no duplicate records of the same id are possible.
1.2.1.3. Input using Bluetooth technology
Another mechanism is used for generation of test cases as this scenario cannot generate users
fast enough for system to be stressed. For that reasons and for more flexibility of the system
another mechanism of input is realized. Idea is simple: instead of manually typing their user ids in
GUI, users can do that by using Bluetooth capabilities of their cellphones. File including patients
id is transferred over Bluetooth connection to certain folder at the computer where GUI is running.
GUI has a mechanism of listening for an event of creating the new file in selected directory of the
computer and it detects transfer of file with patients id. When it is detected, file reader
implemented the GUI opens the file, reads patients id and from that point on processing goes on
as for any normal user who has typed in his/her details in GUI. Initial examination and patient
data for new user are shown in the boxes and data about new user is shown in log box, too.
1.2.2 Data Transfer through Socket Mechanism using TCP/IP The uCsimm/uClinux
combination provides necessary hardware and software for Ethernet connectivity. We use the
Ethernet capabilities through the socket mechanism that supports the client/server interaction
over an internet i.e. over a local lan. The server executes on the linux workstation and the client
on the uCSimm board. When the QMS software is booted, a program is used to establish clientserver connection, and information on incoming patients (n, e) is sent over the network
throughout the program.
On the uCsimm side, the information sent over the link is stored in the socket buffer for reading. A
RTAI FIFO is created in kernel space to read the incoming data for processing.

RTAI provides a variety of mechanism for inter-process communication. Although the Unix system
provides similar IPC mechanism to the user-space processes, RTAI needs to provide its own
implementation for them in order to make it possible for the real-time tasks to use the IPC
mechanism, as they can not use the standard Linux system calls. The different IPC mechanism
are included as Kernel modules, which can be loaded in addition to the basic RTAI and
scheduler-modules only when they are needed by the tasks.
We use the communication mechanism of RTAI FIFO. FIFO is a synchronous and unblocking
one-way communication channel between a Linux process and a real time task having a size
limit. No data can be overwritten in a FIFO, but a FIFO can become full in which new data cannot
be written until the old data is consumed.
When patient data is received in the FIFO, a FIFO handler detects any new incoming data and
passes it to the main program for queue/room allocation.

1.2.3 Room Allocation Mechanism - Two queues are created, one to hold the incoming patients
that are not emergency cases (n) and one to hold emergency cases (e). The incoming data (n,
e) is assigned to the respective queues and the size of each queue at any point of time
represents the number of emergency and normal patients in waiting for being attended by the
doctor.

The system has 4 clinic rooms that can be allocated to patients. To determine fair priority of room
allocation to normal and emergency patients, we use the following algorithm.
p = size_e / (size_n + size_e),
where:
p is used (as below) to determine the priority of emergency patients over normal,
size_e = size of queue holding emergency patients, i.e. no of emergency patients in system
size_n = size of queue holding normal patients, i.e. no of normal patients in the system
If

(p = 0), no_of_room_emergency = 0;
(0 < p <=0.25), no_of_room_emergency = 1;
(0.25 <= p <=0.50), no_of_room_emergency = 2;
(0.50 <= p <=0.75), no_of_room_emergency = 3;
(0.75 <= p <=1.00), no_of_room_emergency = 4;

where no_of_room_emergency is the number of rooms that can be allocated at a single point of
time to emergency patients.
This algorithm determined the number of rooms to be allocated on priority basis to emergency
patients over normal patients, based on the two queue sizes. Hence, when
no_of_room_emergency=2, at most two emergency patients can be assigned rooms, as and
when the rooms become available.
For example; a) size_e=0, all rooms will be allocated for normal patients.
b) Size_e=3, size_n=12, 1 room will be assigned (as and when any room is free) for emergency
patients so that all at most 1 emergency patient will be assigned a room, which becomes
available the first. The second emergency patient will only be assigned the first available room
after the first emergency patient has been attended to.
1.2.4 Process Scheduling/ Task handling The Main program has 3 main tasks:
Task 1 - Fifo handling (to retrieve patient detail incoming over the network link)
Task 2 Reading Data from Buffer and allocating room, display LED
Task 3 Speaker invocation
Task 1 has the highest priority. It is an event driven task. When data is available in the FIFO, the
FIFO handler will start task 1. Task 1 then updates the queue information accordingly.

Task 2 and task 3 are periodic tasks. The Time period has been pre-determined based on
analysis. Task 2 checks the current queue information and room status and allocates the rooms
to the patients. It also controls the LEDs to inform each rooms occupancy. Because interrupts
can change the queue information and room status at any point of time, we need task 2 to check
the queue and room status again once in a while and a periodic task implementation would be the
most suitable choice for task 2 over here.
Task 3 has the lowest priority, and hence whenever any other task is activated, it is pre-empted.
1.2.5 Speaker The Speaker is the lowest priority task in the system. It is scheduled to run
throughout, and is pre-empted by higher priority tasks. Hence a speaker in the off mode indicates
that another task is currently active. A user space program is coded to read from a audio file and
then write the read data into a real time FIFO. Then a kernel module is there to invoke the FIFO
handler and subsequently, the PWM output of the microcontroller is loader with audio samples to
produce sounds.
1.2.6 LEDs The uCsimm board LEDs on Port D is used to display a room occupancy status. If a
room is occupied, the LED assigned will be lighted, and when vacant, the LED assigned will be
off.
1.2.7 Push Buttons and Interrupts - A push button/dip switch is used to indicate when a room
becomes vacant. This is achieved by using hardware interrupts provided by the MC68EZ328
micro-controller. When there are edge sensitive triggers to the Port D inputs, an interrupt service
routine is called to clear the room and update the patient queue status. This is analogous to the
reality where the doctor in each room has a button available to indicate when he is ready for the
next patient. The interrupts handlers are registered in Linux and reflected in /proc/interrupts.
Since there are 3 doctor rooms, 3 pins are used to generate interrupt to indicate that the patient is
leaving the particular room. The LED display will change to vacant accordingly.
1.2.8 Clinic status display Since a kernel module is employed to schedule patients and
update room availability, data needs to be passed to the user application for displaying an overall
clinic status. Therefore, shared memory is employed because of its convenience and ease in
reference. It can be described as an array of storage with each element having the details
needed to display status. The user application is allowed to read from the array and output to
console necessary information that describes the whole clinic status.

10

1.2.9 Patient threshold There will be a limit to how many patients 3 doctors in a clinic can
handle. We are setting a limit using a general semaphore, such that when a new patient arrives, a
semaphore is being taken and if he leaves a semaphore is being posted. The motivation to use a
general semaphore is that adding and subtracting patients from queue are two different tasks
running and semaphores may be used to prevent any concurrency issues.

1.3 SYSTEM MANUAL


The system (at the user level) is made up of 2 components, the server (uCsimm) and the
workstation. The workstation will continuously play a sound if it has no other tasks (indicating an
idle system).

When a patient enters the clinic he will have his details entered into the system GUI at the
workstation. The user will also include whether he requires emergency assistance. Based on his
entry and urgency level, he will be assigned a queue number and automatically directed to an
appropriate room when one becomes available.

The clinic consists of 4 rooms and 4 LEDs (on uCsimm) represent the status of the 4 rooms. An
active LED means the room is occupied. Pressing a button represents the patient has left the
room and will turn off its corresponding LED. In the case the button is not pressed within 15
minutes from the patient entering the room, the patient will also be considered as having left the
room.

11

1.4 SYSTEM FLOWCHART (High level overview)

1.5 Use Case Diagram

12

2. Software Design Features


2.1 I/O facilities

The Queue Management System uses a user friendly GUI, designed in C#, to input patient
details. The GUI runs on the workstation and requests for relevant patient details, including
whether emergency or normal assistance is required.

Inter-process Communication is achieved through socket mechanism using TCP/IP. Sockets


provide a special kind of inter-process communication that emphasizes the distinction between
a server process and client processes. The server and client may be on the same machine or on
different machines connected via ethernet. We use a connection oriented system where sockets
use TCP to connect client to server, and server to client. The Server runs on the uCsimm and
Client runs on the workstation. The server program runs in the background and the GUI passes
information across the link as and when patients come in.

At the uCsimm socket, data is written onto a Fifo structure in the kernel space from where the
data is extracted to enter into respective queues (normal/emergency).

2.2 Process Scheduling

When data is read from the socket, the user space program will write data the a FIFO. The FIFO
handler will then send a command to invoke task 1. After updating the queue information and the
allocation of the total number of room based on the dynamic load balancing algorithm mentioned
above, task 1 stops. When the next period of task 2 is reached, it will use the newly updated
queue information, the room allocation information for each type of patients, and the current
status of the rooms to arrange a new patient into a room if possible.
The period of task 2 (500ms) is designed to be long enough compared to its execution time
(which is in nanoseconds unit) so that the user space process is not starved. When the control is
changed back to the user space program, it will again check the socket and send data to the
kernel space again if it is available.

13

2.3 Inter-process Communication

To communicate between tasks and processes in the system, we use FIFOs, shared memory and
semaphores. The FIFOs are used as a medium to transfer data from the user space to the kernel
module and to send that data around between tasks in the kernel space. The FIFO that connects
the user space and the kernel space has a FIFO handler attached to it to invoke task 1 when data
is available. Other FIFOs in the kernel space do not need immediate responses from the tasks
and therefore no FIFO handlers are attached to them. They are used as normal buffers.

Shared memory & semaphores


Memory space is allocated to the same address space in both kernel and user space and
accessing them from either space gives the same data. This is similar to global variables where
modify one would affect all functions that are running in a program. Note that this must be use
with caution since there are no checks to reading and writing to these shared memory spaces.
This program is avoided since only the kernel module in our system writes and the user program
will only read from it.
Real time semaphores provide a low level blocking or non blocking checks to ensure that there
will be no concurrency problems. In our system, the task that will add new patient to the queue
will take a semaphore before updating the queue. On the contrary, the task that deletes a existing
patient will give a semaphore to allow more patients to enter.

2.4 Deadlock Management


Deadlock is a situation where a group of processes are all blocked and none of them can
become unblocked as the resources will never be released. Our Queue Management System
employs static prevention of deadlock where we restrict type of request and acquisition to make
deadlock impossible. A possibility for a deadlock is at the network communication level but our
program is such that it prevents any such occurrence with the client and server exchanging data
in a uni-direction pattern.

2.5 Mutual Exclusion


To preserve data consistency, shared resources must be accessed in mutual exclusion. When a
process has to read or update certain shared resources, it first enters a critical region to achieve
mutual exclusion and ensure that no other process will use the shared resource at the same time.

14

3 SOFTWARE DESIGN DIAGRAMS

3.1 Data Flow Diagrams


3.1.1

Level 0

3.1.2

Level 1

15

4 TESTING
Under normal testing, different values of n and e are used to test the dynamic load balancing
algorithm. Interrupts are generated to test the response of the system. It shows that rooms are
allocated as expected based on the ratio between n and e. The interrupt service routines also
update the room status information and the queue information correctly whenever interrupts are
triggered.
Under stress testing, data is continuously read and interrupts are continuously triggered. We
expect the queue information update, rooms status update and room allocation to follow the
current rules. It can be observed that our system is actually be able to meet this expectation
under heavy load condition.

16

5 LEARNING OUTCOMES
Augustine Cheong
The pulse-width modulator of the uCsimm was intended to play a tune while the system was not
processing other tasks. We started with using the PWM in tone mode, which causes it to
generate a continuous tone as long as the PWM sample register contains a non-zero value. We
then experimented with having the PWM play a proper sound file instead of a single tone.
While the PWM was able to do so, we were not able to integrate it succesfully with the rest of the
system. With longer sound files, the system would spend more time playing the sound. Since the
system is non-preemptive, the task of playing a long sound file would end up blocking the higher
priority tasks. We were thus unable to implement a schedule such that the higher priority tasks
would not undergo starvation.
One possible solution would be to use a shorter sound file. However, we did not implement this
method as we wanted our system to be able to perform the real-time requirements even with long
sound files.
This project has taught me the difficulties that arise in designing a real-time embedded system,
such as how to deal with the possibility of deadlocks and priority inversion. I can also better
understand the importance of planning an efficient scheduling algorithm to prevent resource
starvation.
The team suffered minor setbacks during the integration phase because there was insufficient
communication between members working on different parts of the system, leading to
incompatible code. More regular updates using the wiki provided would have prevented this from
occuring.

Ritika Taragi
For each implementation, the Linux operating system is run as the lowest priority task of a small
real-time operating system. Linux undergoes no changes to its operation from the standpoint of
the user or the Linux kernel, except that it is permitted to execute only when there are no realtime tasks executing. Both implementations extend the standard Linux programming environment
to real-time problems by allowing real-time tasks and interrupt handlers to communicate with

17

ordinary Linux processes. As the client program is a user space process, there will be problem
running it continuously with our kernel tasks running at the same time. There is only one
processor and if the kernel tasks keep running, the client program will starve, and even if there is
new patient, it would not have the resources to sense it. Hence, the kernel module will be used to
provide service to the user app when there is any data available. The client program will be in a
continuous loop, and once it receives a new patient, it will write some data into a RT FIFO. FIFO
is a synchronous and unblocking one-way communication channel between a Linux process and
a real time task having a size limit. No data can be overwritten in a FIFO, but a FIFO can become
full in which new data cannot be written until the old data is consumed.
. The difference is that the kernel module does not keep running now.
There will be a handler in the kernel to notice the new patient and it will starting executing tasks
like queuing and allocating rooms. Once done, this task goes into a suspend mode, freeing
resources to allow the client program to take in new patient.
Through the project, I understood the use of RT features. I learnt basic mechanism of InterProcess Communication between user space process and RTAI code, principles of process
synchronization through interrupts and event driven mechanism, concepts of mutual exclusion,
and deadlock management.

Huynh Le Trung
After this project, I have learnt much on how a real-time system can be designed using RTAILinux. I have also understood about different real-time scheduling algorithms and the interprocess communication between various processes in the system. Various difficulties that our
group had to face during the implementation of the system gave me many good experiences so
that I could avoid and overcome similar issues in my future projects. In summary, although the
project was not easy and would sometimes become very confusing (partly due to the limitation of
the hardware itself), it was a good introduction to the field of real-time embedded system design.

Tiew Ee Yeong
Through this project, Ive learnt a lot about Linux kernel and writing codes such that it suits both
the Linux API and the RTAI API. The foremost problem encountered will be the difference in
writing kernel modules where Linux system calls, like open() and read() cannot be used. This has
force me to dwell deeper into understanding the Linux kernel where I learnt about some basics
about writing device drivers and some kernel functions. For example, the request_irq() function
was used in this project to register interrupts handler for the microcontroller. With some
understanding of the Linux kernel structure, Ive proceeded to read about what services RTAI
could provide. The most useful service will be the real time FIFO where it serves to communicate

18

data between the kernel and user. I feel that the RTAI FIFO function is like a character device file
in Linux, thus I learnt that RTAI provides a framework to substitute services in Linux kernel ( e.g.
rtf_get() similar to read() ) and the API provided by RTAI are mostly sufficient for the scope of our
project. Ive also learnt about programming loadable modules in which there must have an init
and cleanup function. In these modules, there are running kernel tasks that can be initialized,
suspending, pending or even blocked. Each task has its own work to do and that programming
the tasks such that they do not starve other tasks, know when to suspend and when to wake up
are problems that could take a long time to solve. Therefore, I understood why there are so many
documented synchronizations examples, such as the dining philosophers problem to let people
use them and improve on them since writing one from scratch could be highly error prone.
Overall, I feel that it is a challenge to predict the behavior of a real time system, especially built on
a portable platform like Linux, but I have gained invaluable experiences in programming in both
RTAI and Linux environment.

Mateja Dokleja
Through this project, I have learnt much on how a real-time system can be designed using RTAILinux.I have also learnt the basics of using Bluetooth with C#, to implement a robust
communication system. As our group discussed the project and tested various sections, I learnt
more about real time tasks, their scheduling algorithms, and how the RTAI processes work.

Sahil Gupta
I was in charge of designing the architecture of the project and leading the group.
To work efficiently, we had divided our team into 3 groups: Trung and Me in charge of finding out
how the Board and RTAI work, designing the controller. Ee yeong and Augustine in charge of the
speaker and interrupts, Ritika and Mateja with the communication and GUI.
So apart of designing the algorithm, I was also in charge of co-ordination of activites, assigning
work to members.
My first task was to find out how the uCSimm board really works? What are the hardware
specifications? So we started of with understand what kernel modules, user applications mean.
After getting the simple "hello world" prog we went on to try the task implementation example
given in the RTAI manual. This didn't work as mentioned in the manual for us since our processor
was a Uni Processor and not an SMP.

19

Then we figured out how to write to PortD and switch between tasks. We also found out how to
use the "make periodic function" and it works flawlessly. We were able to implement the task
switching using LEDs as shown in lecture notes.
After experimenting we found out that there is a problem with the hardware. Once a lower priority
task is pre-empted by a higher priority task, it is not restored after the computation time for the
higher priority task is over. So once pre-empted,a task is never restored.
I also came up with the Dynamic Load Balancing algorithm and a simple method to implement
Bluetooth. For the dynamic load balancing algo, The rooms to emergency and normal patients
are allocated based on their relative ratios. So if the percentage of normal patients is less, less
rooms will be allocated to them. But this algorithm gives priority to emergency patients; as long as
there is atleast one emergency patient, a room will be allocated to him, but it is not the same case
with normal patients.

20

Appendix
PROJECT CODE
User Space Module

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/time.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "rtai_shm.h"

// note that a different compileApp.sh is needed

to use rtai functions in userland


#include "control.h"
/*definitions for SHM*/
#define SPACE1 0xAAAA
#define MEMSIZE 20
unsigned int *adr;
#define BUFSIZE 70
char buf[BUFSIZE];
int t;
void print(void) // function to print out the full status of the clinic
{
printf("==================Clinic Status Report====================
\n");

21

printf("

<Patient>

<Room Reserved for>

\n");
printf("Room 1:

%c%d

%c

\n", adr[0],

%c%d

%c

\n", adr[2],

%c%d

%c

\n", adr[4],

adr[1], adr[6]);
printf("Room 2:
adr[3], adr[7]);
printf("Room 3:
adr[5], adr[8]);
printf("---------------------------------------------------------\n");
printf("Total number of normal[n] patients

%d\n",

%d\n",

adr[9]);
printf("Total number of emergency[e] patients
adr[10]);
printf("Total number of patients

%d\n",

adr[9]+adr[10]);
printf("==========================================================
\n");
}
int main(int argc,char *argv[])
{
fd_set rfds;
static unsigned char buf[16384*3];
struct timeval tv;
int fd0, fds, ctl, sFifo, rd_num, wr_num;
struct my_msg_struct msg;
int client_sock_desc, client_len;
struct sockaddr_in client_addr;
char ch;
int result;
unsigned char *ptr;
adr = rtai_malloc(SPACE1, MEMSIZE); // allocate a memory pointing
to the same address as that declared in kernel
printf("Here in app main\n");

22

if ((fd0 = open("/dev/rtf0", O_RDONLY)) < 0) {


fprintf(stderr, "Error opening /dev/rtf1\n");
exit(1);
}
if ((ctl = open("/dev/rtf1", O_WRONLY)) < 0) {
fprintf(stderr, "Error opening /dev/rtf2\n");
exit(1);
}
/*---------------------------------------------------------------------*/
client_sock_desc = socket(AF_INET, SOCK_STREAM, 0);
client_addr.sin_family = AF_INET;
result = inet_aton("192.168.1.1", &client_addr.sin_addr);
if (result == 0)
{
perror("inet_aton error");
return (-1);
}
client_addr.sin_port = htons(4242);
client_len = sizeof(client_addr);
bind(client_sock_desc, (struct sockaddr *)&client_addr,
client_len);
result = connect(client_sock_desc, (struct sockaddr
*)&client_addr, client_len);
if (result == -1)
{
perror("client cannot connect");
return (-1);
}
/*---------------------------------------------------------------------*/
while(1)
{
/***********************************************************
*******/

23

printf(" Awaiting new patients... \n");


while ( read(client_sock_desc, &ch, 1) < 0 );// if there are
no new patient, do nothing {}
/***********************************************************
*******/
/* now start the tasks */
//

printf("New Patient: ");

//

scanf("%c", &ch);
if( ch == 'n' || ch == 'e' )
{
printf(" New patient arrived! \n");
msg.command = START_TASK;
msg.task = 0;
msg.period = 500;
msg.data = ch;
if (write(ctl, &msg, sizeof(msg)) < 0)
{
fprintf(stderr, "Can't send a command to RT-

task\n");
exit(1);
}
msg.task = 1;
msg.period = 700;
msg.data = 'z';
if (write(ctl, &msg, sizeof(msg)) < 0)
{
fprintf(stderr, "Can't send a command to RTtask\n");
exit(1);
}
}
if ( ch == 'p')
print();

// if get p as input, print the status of the

clinic
FD_ZERO(&rfds);
FD_SET(fd0, &rfds);

24

tv.tv_sec = 5;
tv.tv_usec = 0;
}
return 0;
}

Kernel Space (RTAI) Module

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/fs.h>
#include <linux/mm.h>
#include <asm/uaccess.h>
#include <rtai.h>
#include <rtai_sched.h>
#include <rtai_fifos.h>
#include <asm/MC68EZ328.h>
#include <asm/irq.h>
#include "rtai_shm.h"
#include "control.h"
/*definitions for SHM*/
#define SPACE1 0xAAAA
#define MEMSIZE 20
unsigned int *adr;
int countn=0;
int counte=0;
#define PWMIRQ 0x00000080
#define PWM_IRQ_ENABLE 0x40
#define PWM32KHZ 0x00

25

#define PWM16KHZ 0x01


#define PWM8KHZ 0x02
#define REPEAT 0x08
#define PWM_SIZE 1024*16*3
#define MAXSIZE

10

#define SOUNDFIFO 4
MODULE_LICENSE("GPL");
EXPORT_NO_SYMBOLS;
static RT_TASK tasks[3];
static int n = 0, e = 0, nin = 1, ein = 1;
int eOut[3];
int nOut[3];
int roomNo;
int availRooms = 3;
char roomReserved[3];

/* e = reserved for 'e', n = reserved for 'n' */

char roomStatus[3] = {'a', 'a', 'a'};

/* a = available, n = occupied

by 'n', e = occupied by 'e' */


SEM *notfull;
void allocRooms(void);
//static char *data[] = {"Frank ", "Zappa "};
/*
void init_PWM(void)
{
printk("Initialising PWM\n");
PWMC=0x0020; // reset
PWMC = PWMC_EN | PWM32KHZ | REPEAT;
printk("Initilise end\n");
}
*/

26

void myIrq1(int irq, void *dev_id, struct pt_regs * regs)


{
if (roomStatus[0] != 'a') // if empty but interrupt, do nothing or
else availRooms will increase indefintely
{
rt_printk("Doctor in room 1 finishes.\n");
if (roomStatus[0] == 'n')
{
rt_printk("n%d has left room 1\n", nOut[0]);
countn--;
}
else if (roomStatus[0] == 'e')
{
rt_printk("e%d has left room 1\n", eOut[0]);
counte--;
}
roomStatus[0] = 'a';
rt_sem_signal(notfull); // add to semaphore
/* update shared memory */
adr[0]='-';
adr[1]= 0;
adr[9]=countn;
adr[10]=counte;
availRooms++;

// increase available rooms

PDDATA |= 0x01;
//

allocRooms();

// no need to alloc rm anymore

}
ISR |= 0x00010000; /* reset IRQ1 */
}
void myIrq2(int irq, void *dev_id, struct pt_regs * regs)
{
if (roomStatus[1] != 'a') // if empty but interrupt, do nothing or
else availRooms will increase indefintely
{
rt_printk("Doctor in room 2 finishes.\n");
if (roomStatus[1] == 'n')

27

{
rt_printk("n%d has left room 2\n", nOut[1]);
countn--;
//

n--;

// just make room available after patient leaves

}
else if (roomStatus[1] == 'e')
{
rt_printk("e%d has left room 2\n", eOut[1]);
counte--;
//

e--;

// just make room available after patient leaves

}
roomStatus[1] = 'a';
rt_sem_signal(notfull);// add to semaphore
/* update shared memory */
adr[9]=countn;
adr[10]=counte;
adr[2]='-';
adr[3]=0; // ascii for '-'
availRooms++;

// increase available rooms

PDDATA |= 0x02;
//

allocRooms();

// no need to alloc rm anymore

}
ISR |= 0x00020000; /* reset IRQ2 */
}
void myIrq3(int irq, void *dev_id, struct pt_regs * regs)
{
if (roomStatus[2] != 'a') // if empty but interrupt, do nothing or
else availRooms will increase indefintely
{
rt_printk("Doctor in room 3 finishes.\n");
if (roomStatus[2] == 'n')
{
rt_printk("n%d has left room 3\n", nOut[2]);
countn--;
//

n--;
}
else if (roomStatus[2] == 'e')

28

{
rt_printk("e%d has left room 3\n", eOut[2]);
counte--;
//

e--;
}
roomStatus[2] = 'a';
rt_sem_signal(notfull);// add to semaphore
/* update shared memory */
adr[9]=countn;
adr[10]=counte;
adr[4]='-';
adr[5]=0; // ascii for '-'
availRooms++;

// increase available rooms

PDDATA |= 0x04;
//

allocRooms();

// no need to alloc rm anymore

}
ISR |= 0x00040000; /* reset IRQ3 */
}
/*
void play_pwm(int fifo)
{
//

unsigned int status;

//

int i, j = 0;
int taskno = fifo;
struct my_msg_struct msg;
while (1)
{
if ((rtf_get(taskno + COMMAND_FIFO + 1, &msg, sizeof(msg)) )

== sizeof(msg) )
{
switch (msg.command)
{
case START_TASK:
rt_task_make_periodic_relative_ns(rt_whoami(), 0,
msg.period*1000000);

29

break;
default:
rt_printk("RTL task: bad command\n");
}
}
i = rtf_get(SOUNDFIFO, pwm_buf, sizeof(pwm_buf));
if (i < 0)
rt_printk("get data failed\n");
play_pwm(i);
next = pwm_buf;
PWMS = *(next++);
while (j < i)
{
status = PWMC;
status &= 0x0020;
if (status!= 0)
{
PWMS=*(next++);
j++;
}
}
PWMC = 0x0020;
PWMS = 0x0A0A;
rt_busy_sleep(200000000);
PWMS = 0x0000;
rt_task_suspend(rt_whoami());
}
}
*/
void allocRooms(void)
{
/* Dynamic allocation of the number of rooms based on the ratio
between 'n' and 'e' cases */
if (e == 0)
{
roomReserved[0] = 'n';
roomReserved[1] = 'n';

30

roomReserved[2] = 'n';
adr[6] = 'n';
adr[7] = 'n';
adr[8] = 'n';
}
else if (0 < e && (3 * e) <= (n + e))
{
roomReserved[0] = 'e';
roomReserved[1] = 'n';
roomReserved[2] = 'n';
/* update shared memory */
adr[6] = 'e';
adr[7] = 'n';
adr[8] = 'n';
}
else if ((3 * e) > (n + e) && (3 * e) <= (2 * (n + e)))
{
roomReserved[0] = 'e';
roomReserved[1] = 'e';
roomReserved[2] = 'n';
/* update shared memory */
adr[6] = 'e';
adr[7] = 'e';
adr[8] = 'n';
}
else if ((3 * e) > (2 * (n + e)))
{
roomReserved[0] = 'e';
roomReserved[1] = 'e';
roomReserved[2] = 'e';
/* update shared memory */
adr[6] = 'e';
adr[7] = 'e';
adr[8] = 'e';
}
return;
}

31

static void getData(int fifo)


{
int taskno = fifo;
struct my_msg_struct msg;
while (1)
{
if ((rtf_get(taskno + COMMAND_FIFO + 1, &msg, sizeof(msg)) )
== sizeof(msg) )
{
switch (msg.command)
{
case START_TASK:
rt_task_make_periodic_relative_ns(rt_whoami(), 0,
msg.period*1000000);
break;
default:
rt_printk("RTL task: bad command\n");
}
}

if (msg.data == 'e')
{
rt_sem_wait(notfull);

// take a semaphore with every

patient coming in
e++;
counte++;
}

else if (msg.data == 'n')


{
rt_sem_wait(notfull);

// take a semaphore with every

patient coming in
n++;
countn++; // update total number of patients
}

32

adr[9]=countn;
adr[10]=counte;
allocRooms();
if (rt_whoami() == &tasks[0])
rtf_put(fifo, &e, sizeof(int));
rt_task_suspend(rt_whoami());
rt_task_wait_period();
}
}
static void allocateRooms(int fifo)
{
int taskno = fifo;
int adjnum=0;
struct my_msg_struct msg;
while (1)
{
if (rtf_get(taskno + COMMAND_FIFO + 1, &msg, sizeof(msg)) ==
sizeof(msg))
{
switch (msg.command)
{
case START_TASK:
rt_task_make_periodic_relative_ns(rt_whoami(), 0,
msg.period*1000000);
break;
default:
rt_printk("RTL task: bad command\n");
}
}
for (roomNo = 0; roomNo < 3; roomNo++)
{
if (e > 0 && roomReserved[roomNo] == 'e' &&
roomStatus[roomNo] != 'e')
{

33

if (roomStatus[roomNo] == 'n')
{
if (availRooms == 0)
{
n++;
roomStatus[roomNo] = 'e';
e--;

// decrease the number in

queue after allocating room


allocRooms();
}
//

else { /*should continue loop since there

are availroom n will find one eventually*/


//

roomStatus[3 - availRooms] = 'e';

//

e--;

// decrease the number in

queue after allocating room


//

availRooms--;

//

}
} else {
roomStatus[roomNo] = 'e';

// if room

is not e not n but a, just occupy it


availRooms--;
e--;

// decrease the number in queue

after allocating room


allocRooms();
}
if (roomNo == 0)
PDDATA &= 0xFE;
else if (roomNo == 1)
PDDATA &= 0xFD;
else if (roomNo == 2)
PDDATA &= 0xFB;
if (roomStatus[roomNo]=='e')
{ // only when e finds a room, status is updated

34

rt_printk("e%d is now in room %d\n", ein,


roomNo+1);
eOut[roomNo]=ein;
/* update shared memory, adjust to make
sure numbers tally */
if (roomNo==0) adjnum=0;
else if (roomNo==1) adjnum=2;
else if (roomNo==2) adjnum=4;
adr[adjnum] = 'e';
adr[adjnum+1] = ein;
ein++;
}
}
if (n > 0 && roomReserved[roomNo] == 'n' &&
roomStatus[roomNo] == 'a')
{
roomStatus[roomNo] = 'n';
availRooms--;
n--;

// decrease the number in queue after

allocating room
allocRooms();
if (roomNo==0) adjnum=0;
else if (roomNo==1) adjnum=2;
else if (roomNo==2) adjnum=4;
/* update shared memory, adjust to make sure
numbers tally */
if (roomNo == 0)
PDDATA &= 0xFE;
else if (roomNo == 1)
PDDATA &= 0xFD;
else if (roomNo == 2)
PDDATA &= 0xFB;
rt_printk("n%d is now in room %d\n", nin,
roomNo+1);
nOut[roomNo]=nin;

35

adr[adjnum] = 'n';
adr[adjnum+1] = nin;
nin++;
}
}
//

rtf_put(taskNo, data[taskNo], 6);

//

rt_task_suspend(rt_whoami());
rt_task_wait_period();
}

}
static int my_handler(unsigned int fifo, int rw)
{
struct my_msg_struct msg;
int err;
if (rw == 'r')
{
return -EINVAL;
}
while ((err = rtf_get(COMMAND_FIFO, &msg, sizeof(msg))) ==
sizeof(msg))
{
rtf_put(msg.task + COMMAND_FIFO + 1, &msg, sizeof(msg));
rt_task_resume(&tasks[msg.task]);
}
if (err != 0)
{
return -EINVAL;
}
return 0;
}

int init_module(void)
{
int i;

36

rtf_create(0, 4000);
rtf_create(1, 4000);
rtf_create(2, 4000);
rtf_create(3, 4000);
//

rtf_create(4, 2000);
PDDIR = 0x0F;
PDSEL = 0x0F;
PDDATA |= 0x0F;
ICR |= 0xEE00;

// IRQs 1,2,3 positive polarity and edge

triggered
//

init_PWM();
/*Request IRQ1, 2 and 3*/
if (request_irq(16, myIrq1, IRQ_FLG_LOCK, "irq1", NULL))
{
rt_printk("Error:irq1 not set \n");
return -1;
}
if (request_irq(17, myIrq2, IRQ_FLG_LOCK, "irq2", NULL))
{
rt_printk("Error:irq2 not set \n");
return -1;
}
if (request_irq(18, myIrq3, IRQ_FLG_LOCK, "irq3", NULL))
{
rt_printk("Error:irq3 not set \n");
return -1;
}
/* initialise share memory and put default values into them */
adr = rtai_kmalloc(SPACE1, MEMSIZE);
i=0;
while ( i != MEMSIZE )
{
adr[i]='-';
i++;
}
adr[1]=adr[3]=adr[5]=0;

37

adr[9]=adr[10]=0;
rt_task_init(&tasks[0], getData, 0, 1000, 0, 0, 0);
rt_task_init(&tasks[1], allocateRooms, 1, 1000, 2, 0, 0);
//

rt_task_init(&tasks[2], play_pwm, 2, 1000, 1, 0, 0);


rtf_create_handler(COMMAND_FIFO, X_FIFO_HANDLER(my_handler));
/*Semaphore init*/
rt_sem_init(notfull, MAXSIZE);
rt_set_oneshot_mode();
start_rt_timer_ns(10000000);
return 0;

void cleanup_module(void)
{
PDDATA |= 0x0F;
rtf_destroy(0);
rtf_destroy(1);
rtf_destroy(2);
rtf_destroy(3);
free_irq(16, NULL);
free_irq(17, NULL);
free_irq(18, NULL);
stop_rt_timer();
rt_task_delete(&tasks[0]);
rt_task_delete(&tasks[1]);
rtai_kfree(SPACE1);
}

Speaker User Space Module


#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>

38

int main(int argc, char *argv[]) {


int rd_num, wr_num, fd, fifo_id;
static unsigned char buf[16384*3];
unsigned char *ptr;
if (argc<2)
{
printf("Please specify files to use\n");
return 0;
}
printf("Opening sound file and FIFO\n");
if ( (fifo_id = open("/dev/rtf1", O_WRONLY)) < 0) {
printf("Error opening write file\n");
return 0;

//open FIFO of minor number 1

if ( (fd=open("argv[2]", O_RDONLY)) < 0 )


printf("error opening file\n");
while ((rd_num = read(fd, buf, sizeof(buf))) > 0) { //while entire
audio file is not yet read
ptr=buf; //points to current buffer position
printf("reading\n");
do { //loop until everything that is read is written into
FIFO
if ( (wr_num=write(fifo_id, ptr, rd_num)) > 0) //if
writing the number of bytes read to FIFO is successful
{
ptr+=wr_num; //points to next data
rd_num-=wr_num; //decrease available bytes to
write
} else { //else write failed
printf("Error in writing to FIFO\n");

39

return 0;
}
printf("Write success\n");
} while(rd_num>0);
}
printf("EXIT from user\n");
return 0;
}

Speaker Kernel Space Module


#include <asm/MC68EZ328.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <rtai_fifos.h>
#include <rtai.h>
#define PWMIRQ 0x00000080
#define PWM_IRQ_ENABLE 0x40
#define PWM32KHZ 0x00
#define PWM16KHZ 0x01
#define PWM8KHZ 0x02
#define REPEAT 0x08
#define FIFO_num 1
#define PWM_SIZE 1024*16*3
unsigned short *next;
unsigned short pwm_buf[PWM_SIZE];
void init_PWM(void)
{
printk("Initialising PWM\n");
PWMC=0x0020; // reset
PWMC = PWMC_EN | PWM32KHZ | REPEAT; // 32KHZ sampling with 3
repeats
printk("Initilise end\n");

40

}
void play_pwm(int i)
{
unsigned int status;
int j=0;
next = pwm_buf;
PWMS=*(next++);
while(j < i)

// to make sure all bytes are put into PWM

output
{
status = PWMC;

// get PWMC status

status &= 0x0020; //check for FIFOAV bit


if ( status!= 0)

// if bit=1 then put one byte into PWMS

{
PWMS=*(next++);
j++;
}
}
}
int data_in(unsigned int fifo)
{
int i;
printk("In FIFO handler\n");
i = rtf_get(FIFO_num, pwm_buf, sizeof(pwm_buf));

// read FIFO

if(i<0)printk("get data failed\n");


play_pwm(i);
return 0;
}
int init_module(void) {
rtf_create(FIFO_num, PWM_SIZE);

// create a new FIFO buffer

rtf_create_handler(FIFO_num, data_in);

// call data_in every

time there is new data


printk("Opening file and attempt to play\n");

41

init_PWM();
return 0;
}
void cleanup_module(void) {
PWMC=0x0020;

// reset

rtf_destroy(FIFO_num);

// delete FIFO

printk("The End\n");
}

42

You might also like