Art-Net Node: Development and implementation of a

Transcription

Art-Net Node: Development and implementation of a
DEPARTEMENT INDUSTRIËLE WETENSCHAPPEN
MASTER IN DE INDUSTRIËLE WETENSCHAPPEN:
ELEKTRONICA-ICT
Art-Net Node: Development and
implementation of a routing device
for lighting control
Masterproef voorgedragen tot het
behalen van de graad en het diploma
van master / industrieel ingenieur
Academiejaar 2008-2009
Door: Rafaël Verbiest
Maarten Reekmans
Promotor hogeschool: dr.ir. T. Goedemé
Promotor bedrijf: ing. K. Buys
ii
Preface
First and foremost, we would like to thank our company promoter, Koen Buys, for presenting us with the opportunity and the necessary tools to carry out this work. Without his
dedication to the subject, his expertise and guidance throughout the last eight months,
we would not have reached our goals. Secondly, our school promoter, Toon Goedemé,
helped tremendously by proofreading this text and directing us to improve its structure.
We, the authors of this book, viewed the challenges of this assignment as an interesting way
to expand our knowledge of embedded systems and low-level drivers. During the progress
of the work, we came into contact with new concepts that improved our understanding
of digital systems and communication protocols. Since we both have a personal interest
in Unix systems, we could ideally put our experience to good use. At the same time, the
challenges we faced helped us learn many new things.
Maarten Reekmans and Rafaël Verbiest
iv
Abstract
English
Very often, a master’s thesis has the goal of proving or disproving a theory or concept.
That is why this document is not a typical thesis; its goal is to create a finished product,
based on the findings of previous works. In recent years, several student projects, dissertations or “end works” at the De Nayer Institute have created several important building
blocks we could use. One of the authors of this thesis, Maarten Reekmans, experimented
in 2006-2007 with the installation of a boot loader, kernel and userland on a self-designed,
ARM-based PCB. Koen Buys, our company promoter, created his own PCB for educational purposes. This PCB was later adapted by him to serve as our hardware platform.
Thomas Langewouters wrote a special Linux driver during his bachelor’s thesis. These
stand-alone works, although successful, had no real usefulness other than to prove a concept. Our goal was to take these building blocks and findings, and use them to produce
a working, useful product for the entertainment industry. This device would transport
a well-known protocol for lighting control (DMX512) over standard LAN Ethernet, and
would be configured through a web interface.
vi
vii
Dutch
De meeste thesissen doelen op het bewijzen of breken van een hypothese. Daarin onderscheidt dit document zich, in de zin dat het een afgewerkt produkt poogt te bouwen,
gebaseerd op ervaringen en conclusies van eindwerken uit de vorige jaren. Zo zijn er
verschillende eindwerken, projecten en thesissen uit afgelopen jaren die cruciale bouwstenen hebben gevormd voor de uitwerking van dit project. Eén van de schrijvers van
deze thesis, Maarten Reekmans, experimenteerde in 2006-2007 met de installatie van een
bootloader, kernel en userland op een zelfgetekende, ARM-gebaseerde PCB. Koen Buys,
onze bedrijfspromotor maakte eveneens een eigen PCB, zij het met educatieve doeleinden. Een aangepaste versie van dat bord vormt het hardwareplatform voor ons eindwerk.
Thomas Langewouters schreef een specifieke driver als bachelorthesis. Deze geslaagde
werken bewezen hun alleenstaande theorieën of concepten, maar werden niet omgezet in
nuttige toepassingen. Onze doelstelling was het bouwen van een werkend en nuttig product voor de entertainmentindustrie, vanuit de ervaringen en bevindingen van voorgaande
projecten. Dit toestel zou een gevestigd standaardprotocol voor verlichting (DMX512)
transporteren over standaard LAN Ethernet, waarbij configuratie mogelijk is met een
webinterface.
viii
Samenvatting
De entertainment industrie gebruikt een traditioneel busnetwerk voor de sturing van lichten, dimmers en rookmachines. Dit netwerk, gestuurd vanuit lichttafels, heet DMX512.
Het probleem bij moderne theatervoorstellingen en concerten is dat met DMX slechts
een beperkt aantal toestellen kunnen aangestuurd worden per kabelnetwerk (universe).
Met de hedendaagse grote concerten en theatervoorstellingen worden steeds meer en meer
toestellen gebruikt. Dit betekent dat een lichttafel enorme hoeveelheden DMX universes
moet kunnen aansturen. Dit brengt een onoverzichtelijke wirwar van kabels met zich mee
die telkens tot aan de lichttafel moeten worden doorgetrokken. In de praktijk is dat dikwijls moeilijk haalbaar. Om dat probleem op te vangen bestaan er protocollen, zoals onder
andere Art-Net, die DMX over ethernet transporteren. Op deze manier kunnen meerdere
universes over één fysieke kabel gestuurd worden tot aan een Node die de signalen opnieuw omzet naar DMX. Het onderwerp van deze thesis is de effectieve implementatie
van een Art-Net Node met 1 Art-Net poort en 4 DMX poorten. De Art-Net Node kan
Art-Net pakketten ontvangen en omzetten naar DMX, en omgekeerd. Bovendien worden
de pakketten geroute naar het correcte netwerk.
De ontwikkeling van de Art-Net Node brengt vele aspecten met zich mee. Als eerste moet
een adequaat hardwareplatform gevonden worden voor specifieke toepassingen. Hiervoor
wordt gebruik gemaakt van een ingebed systeem. Dat systeem vergt configuratie en systeemsoftware zodat de uiteindelijke applicatie kan uitgevoerd worden. Als tweede aspect
moet de effectieve routing en vertalingssoftware geschreven worden met de bijbehorende
configuratie-interface. Tenslotte moet het geheel goed gedocumenteerd worden.
De ontwikkeling van de Art-Net Node kan daarom opgedeeld worden in 3 verschillende
stukken:
• Het ingebed systeem en de nodige configuratie.
• Het schrijven van de software.
• De opstellen van documentatie rond het geheel.
Ingebed Systeem
De Art-Net Node is een toestel met zeer specifieke taken. Daarom gaan we niet gebruik
maken van een persoonlijke computer om de software op uit te voeren. In plaats daarvan
wordt er een toegespitst ingebed platform gebruikt. Dit ingebed systeem leent zich beter
x
tot specifieke taken, zoals het routen en vertalen van signalen. Bovendien is een ingebed
systeem veelal kleiner, beter inbouwbaar, energiezuiniger en goedkoper.
Het ingebed systeem voor de Art-Net Node is een herontwerp van een printplaat gemaakt door onze bedrijfspromotor voor zijn eigen masterproef, gebaseerd op de ARM9
architectuur. Omdat we gebruik maken van een herontwerp, moest alvorens het ingebed systeem gebruikt kon worden de nodige configuratie gebeuren. Het systeem moet
immers initieel door zogenaamde bootloaders opgestart worden en een Linux-gebaseerd
besturingssysteem laden. Deze verschillende stappen en hun bijpassende software moeten
afzonderlijk geconfigureerd en aangepast worden zodat ze samenwerken met de hardware op ons gepersonaliseerd systeem. Bovendien moest voor de DMX-communicatie nog
een speciale driver geïntegreerd worden in het besturingssysteem. Deze driver werd reeds
eerder ontworpen door Thomas Langewouters in het kader van een bachelor eindwerk.
Een ander aspect van het werken met een ingebed systeem is de het kruis-bouwen van
software. De software bouwen op het ingebed systeem zelf zou veel tijd in beslag nemen
omdat de processor veel minder krachtig is dan een hedendaagse persoonlijke computer.
Daarom kruis-bouwen we de software voor het ingebed systeem op een gast PC en kopiëren we daarna de software over. Omdat de gast PC een verschillende architectuur heeft
aan het ingebed systeem moet een volledig kruis-bouw systeem geïnstalleerd worden. In
dit systeem zit een kruis-compiler, een linker, binaire werkapplicaties en een applicatiedebugger.
Applicatie
Wanneer de systeemsoftware van het ingebedde systeem correct werkt kan overgegaan
worden tot het ontwerpen van de effectieve hoofdapplicatie van de Art-Net Node. Deze
applicatie zal data binnennemen, vertalen waar nodig en terug uitzenden op het correcte
netwerk. Het hoofdprogramma heeft 4 modi waarin het kan opereren:
1. Art-Net naar DMX: Via de ethernet poort komen Art-Net pakketten binnen die
worden uitgepakt tot DMX frames. Deze frames worden vervolgens uitgestuurd
naar het correcte universe.
2. DMX naar Art-Net: Eén of meerdere DMX frames worden binnengenomen en geëncapsuleerd in een Art-Net pakket. Dat pakket wordt dan uitgestuurd via de ethernet
poort.
3. DMX naar DMX: Een DMX universe wordt binnengenomen en daarna uitgestuurd
naar één of meerdere DMX universes. Op deze manier wordt een herhalingsmodus
voorzien.
4. Gemixt: Een combinatie van de voorgaande modi, waar simultaan DMX en ArtNet de ingangspunten zijn en de routing op flexibele wijze naar naar zowel DMX
als Art-Net gebeurt.
De Art-Net Node heeft bepaalde instellingen nodig voordat ze kan ingezet worden in een
netwerk. Onder andere het IP-adres en subnetmask moeten ingesteld worden en bovendien
xi
moet ook aangegeven worden hoe de Art-Net en DMX poorten worden verbonden met
elkaar. Om dit op een gebruiksvriendelijke manier te doen wordt er gewerkt met een
webinterface. Deze webinterface zal via een webpagina een visuele gebruikersinterface
aanbieden waar op een overzichtelijke manier de verschillende parameters ingesteld kunnen
worden. Omdat er gebruik gemaakt wordt van een webpagina kunnen de instellingen
veranderd worden vanop eender welk toestel met een webbrowser, zoals een laptop, PDA
of smartphone.
Documentatie
Het idee achter dit eindwerk is dat broncode van de Art-Net Node volledig wordt vrijgegeven onder de GNU General Public License en dat de hardwareontwerpen exclusief
door KB Design worden beheerd. Op deze manier kan de software nog verder uitgebreid
worden door andere software ontwikkelaars zoals andere studenten of eventueel vrijwilligers. Natuurlijk moet de broncode en haar werking ook goed gedocumenteerd zijn zodat
de drempel niet te hoog ligt voor de mensen die het werk verderzetten. De broncode die
we zelf geschreven hebben is daarom met veel “in lijn” commentaar voorzien. We hebben
ook een Wiki pagina met extra informatie over alle software componenten beschikbaar
gemaakt [1]. Bovendien zit in dit document zelf ook veel informatie die nuttig kan zijn
voor het verdere ontwikkelen van de Art-Net Node.
xii
Contents
Preface
iii
Abstract
v
English . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
v
Dutch . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . vii
Samenvatting
ix
List of abbreviations
xvii
1 Introduction
1
1.1
Definition of a problem
. . . . . . . . . . . . . . . . . . . . . . . . . . . .
1
1.2
Description of the project . . . . . . . . . . . . . . . . . . . . . . . . . . .
2
1.3
Overview of this text . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
2
2 Communication Protocols
2.1
DMX512
5
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
5
2.1.1
History . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
5
2.1.2
The standard . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
6
2.1.3
Packet format . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
6
2.1.4
DMX512 electrically . . . . . . . . . . . . . . . . . . . . . . . . . .
9
2.2
DMX Over Ethernet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
2.3
Art-Net . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
2.3.1
Address assignment . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
2.3.2
Modes of operation . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
2.3.2.1
Broadcast mode . . . . . . . . . . . . . . . . . . . . . . . 13
2.3.2.2
Unicast mode . . . . . . . . . . . . . . . . . . . . . . . . . 15
xiv
2.3.2.3
2.4
Packet types . . . . . . . . . . . . . . . . . . . . . . . . . 15
Conclusion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
3 Embedded Hardware and Software
3.1
3.2
Embedded hardware . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
3.1.1
System on chip . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
3.1.2
The Art-Net Node prototype board
3.1.3
Input and output . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
3.1.4
Memory . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
3.1.5
Power supply . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
OS type and kernel . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
Conclusion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28
4 Host Configuration
4.1
Setting the environment . . . . . . . . . . . . . . . . . . . . . . . . 30
Debugging . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
4.2.1
4.2.2
4.3
29
The toolchain . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
4.1.1
4.2
. . . . . . . . . . . . . . . . . 21
Embedded software . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
3.2.1
3.3
19
GNU Debugger . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
4.2.1.1
Remote debugging: target . . . . . . . . . . . . . . . . . . 32
4.2.1.2
Remote debugging: host . . . . . . . . . . . . . . . . . . . 33
Boundary scan (JTAG) . . . . . . . . . . . . . . . . . . . . . . . . . 33
Conclusion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34
5 Platform configuration
35
5.1
On chip bootloader . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
5.2
Primary bootloader . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36
5.3
Secondary bootloader . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38
5.4
5.5
5.3.1
Configuration of U-Boot . . . . . . . . . . . . . . . . . . . . . . . . 39
5.3.2
Using the U-Boot bootloader . . . . . . . . . . . . . . . . . . . . . 39
OS kernel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40
5.4.1
Configuration of the Linux kernel . . . . . . . . . . . . . . . . . . . 41
5.4.2
Building and booting the kernel . . . . . . . . . . . . . . . . . . . . 42
Linux distribution . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43
xv
5.6
5.5.1
Buildroot . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44
5.5.2
Debian GNU/Linux . . . . . . . . . . . . . . . . . . . . . . . . . . . 45
5.5.3
Distribution on the Art-Net Node . . . . . . . . . . . . . . . . . . . 45
Conclusion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47
6 DMX Driver Integration
49
6.1
DMX driver . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49
6.2
Platform devices . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50
6.3
Theory of operation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51
6.3.1
Transmitting . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52
6.3.2
Receiving . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52
6.4
Simple testing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52
6.5
Advanced testing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54
6.6
Conclusion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56
7 Art-Net Router Software
57
7.1
Design basics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57
7.2
Controller thread . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60
7.3
Control protocol . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60
7.4
Worker thread . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61
7.5
Conclusion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63
8 Web interface
65
8.1
Webserver . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65
8.2
Configuration page . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 67
8.3
Conclusion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 67
9 Achievements
71
10 Conclusion
73
Bibliography
74
Appendices
81
A Technical Specifications
81
xvi
B Errata
83
B.1 Software bugs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83
B.2 Hardware bugs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83
C Code Listings
85
List of abbreviations
ACN Architecture for Control Networks
ARM Advanced RISC Machine
CPU Central Processing Unit
DBGU Debug Unit Serial Port
DFU Device Firmware Upgrade
DHCP Dynamic Host Configuration Protocol
DIP Dual In-line Package
DMA Direct Memory Access
DMX Digital Multiplexed
EBI External Bus Interface
EEPROM Electrically Erasable Programable Read Only Memory
GCC Gnu Compiler Collection
GPIO General Purpose Input Output
GPL GNU General Public License
ICE In Circuit Emulator
IDE Integrated Development Environment
IP Internet Protocol
ISP In System Programming
JTAG Joint Test Action Group
LAN Local Area Network
LCD Liquid Crystal Display
MAB Mark After Break
xviii
MAC Media Access Layer
MIPS Millions Of Instructions Per Second
MMU Memory Management Unit
MTBF Mark Time Between Frames
MTBP Mark Time Between Packets
NFS Network File System
OEM Original Equipment Manufacturer
OS Operating System
PCB Printed Circuit Board
PDC Peripheral DMA Controller
PID Process Identity
POSIX Portable Operating System Interface
PQFP Plastic Quad Flat Pack
RAM Random Access Memory
RDM Remote Device Management
RFC Request For Comments
RISC Reduced Instruction Set Computing
RTOS Real Time Operating System
SC Start Code
SoC System On Chip
SPI Serial Peripheral Interface
SSC Serial Synchronous Controller
TCP Transmission Control Protocol
TFTP Trivial File Transfer Protocol
TWI Two Wire Interface
UART Universal Asynchronous Receiver/ Transmitter
UDP User Datagram Protocol
USB Universal Serial Bus
USITT United States Institute For Theatre Technology
XLR Cannon X connector with Latch and Rubber
List of Figures
1.1
Typical DMX devices . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1
1.2
Art-Net Node’s modes of operation . . . . . . . . . . . . . . . . . . . . . .
3
2.1
DMX Timing Diagram . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
8
2.2
Common mode voltages in twisted pair cable . . . . . . . . . . . . . . . . .
9
2.3
DMX512 female XLR pin-out . . . . . . . . . . . . . . . . . . . . . . . . . 10
2.4
5-pin XLR connectors
2.5
Example of an Art-Net network . . . . . . . . . . . . . . . . . . . . . . . . 13
2.6
ArtDmx packet format . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
3.1
Hardware diagram . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
3.2
Art-Net node prototype board . . . . . . . . . . . . . . . . . . . . . . . . . 22
3.3
Ethernet Connection Diagram . . . . . . . . . . . . . . . . . . . . . . . . . 25
4.1
Workflow Cross-Compilation . . . . . . . . . . . . . . . . . . . . . . . . . . 30
4.2
Workflow Remote Debugging
4.3
JTAG Chain . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34
4.4
USB JTAG debugger . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34
5.1
Stages of the boot process . . . . . . . . . . . . . . . . . . . . . . . . . . . 36
5.2
Flowchart bootsequence . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
5.3
Linux Architecture . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41
5.4
Linux Kernel Menuconfig . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42
5.5
Buildroot Menuconfig . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46
6.1
Operation of the driver state machine in write mode . . . . . . . . . . . . . 51
6.2
Function and situation of the character devices . . . . . . . . . . . . . . . . 53
6.3
Operation of the driver state machine in read mode . . . . . . . . . . . . . 53
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
. . . . . . . . . . . . . . . . . . . . . . . . . 32
xx
6.4
Zoomed-out view of a DMX frame as sent by the driver . . . . . . . . . . . 54
6.5
Detailed view of the beginning of an all-zero frame as sent by the driver . . 54
6.6
Detailed view of the beginning of a 0xa frame as sent by the driver . . . . 55
7.1
Flowchart of the main application . . . . . . . . . . . . . . . . . . . . . . . 59
7.2
Flowchart of the DMX routing algorithm . . . . . . . . . . . . . . . . . . . 62
7.3
Flowchart of the Art-Net routing algorithm . . . . . . . . . . . . . . . . . . 63
8.1
Flowchart Configuration Update (PHP/Server side) . . . . . . . . . . . . . 66
8.2
Configuration Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . 67
8.3
AJAX and Classic Webapplication Models . . . . . . . . . . . . . . . . . . 68
Chapter 1
Introduction
1.1
Definition of a problem
DMX (Digital Multiplexed) is a communications protocol, designed in the 1980’s. It is
still commonly used to control stage lighting and various effects with a lighting desk.
A typical example of these kind of lights and lighting desks can be found in figure 1.1.
Initially DMX controlled only limited setups, but over the years the requirements and
number of possible uses grew and unfortunately some problems arose.
(a) DMX controlled stage
light
(b) DMX controller
Figure 1.1: Typical DMX devices
Control data size: At large events, the maximum number of devices that could be
2
Introduction
controlled by DMX proved insufficient.
Electrical capability: Due to a limitation of the electrical specification (further explained in 2.1.4), only 32 devices per DMX line can be connected in practice without
the use of a repeater device, further shrinking its scaling capability.
Distance: In some cases, the distance between the control desk and the devices would
be greater than the allowed maximum specified by the DMX protocol.
Cabling: When more and more far-away devices needed to be controlled, a large number
of parallel cables would need to be put into place, making the logistics very difficult.
To solve these scalability issues, new protocols were developed which use Ethernet to
transport DMX data. Using Ethernet, many of the limitations of DMX can be resolved.
The lighting desks now have Ethernet connectors instead of DMX plugs, and that means
that a converter is needed to control legacy lighting equipment.
1.2
Description of the project
It is the kind of converter described in section 1.1 that provides the subject for this
master’s thesis. Our converter is to have at least one Ethernet port and four DMX ports.
The device will not only convert Ethernet to DMX but will also have routing capabilities.
The modes of operation that the device will be capable of are drawn in figure 1.2.
• In the first mode of operation, seen in figure 1.3(a), our device will translate Ethernet
signals to DMX.
• The second mode, seen in figure 1.3(b), does the opposite: translate from legacy
DMX signals to Ethernet.
• The third mode, seen in figure 1.3(c), is called mixed mode because it combines the
two first modes.
• The fourth mode, seen in figure 1.3(d), does not use Ethernet and makes the device
act only as a DMX repeater.
The name “Art-Net Node”, used in the figures, comes from the name of the Art-Net
protocol, which we will use to transport DMX data over Ethernet. The choice of this
protocol will be explained later.
1.3
Overview of this text
Chapter 2 covers our research into the various communication protocols we needed to
understand in order to build our device. It also covers some of the decisions we
made regarding these protocols.
1.3 Overview of this text
3
(a) Art-Net to DMX mode
(b) DMX to Art-Net mode
(c) Mixed Mode
(d) Repeater mode
Figure 1.2: Art-Net Node’s modes of operation
Chapter 3 explains the important building blocks of the system, discussing hardware as
well as software.
Chapter 4 describes the necessary tools for building and debugging software for embedded
platforms.
Chapter 5 details the steps we took to install an operating system on our platform,
thereby providing a proper environment for our own application.
Chapter 6 discusses the changes we made to parts of that operating system so that it
would allow the easy use of the DMX protocol.
Chapter 7 talks about the main application we wrote and gives a description of how its
algorithms work. It also discusses the interaction with the operating system and
libraries.
Chapter 8 explains the user interface that will be used to configure the main application.
4
Introduction
Finally, we close off the work by talking about our achievements, in chapter 9, and draw
a general conclusion in chapter 10.
Chapter 2
Communication Protocols
This chapter is the summary of our theoretical research into the different communication
standards we would need to understand in order to meet the design goal of the Art-Net
Node.
Section 2.1 explains the inner workings of the DMX512 protocol, and points out its
limitations.
Further on, in section 2.2 we research the possible solutions to overcome these limitations
by transporting DMX data over Ethernet.
Out of the options researched in section 2.2, we chose the Art-Net protocol as the carrier
for DMX over Ethernet. This protocol is analysed in section 2.3.
2.1
DMX512
This section explains the basics of the DMX512 communication protocol. First, some
concepts are explained, after which the digital part of the protocol is discussed, followed
by an explanation of the analog part.
2.1.1
History
DMX512 was first developed in 1986 by the U.S. Institute of Theatre Technology (USITT)
[2]. It was conceived as a solution to interface lighting control panels with dimmers. In
the first couple of years a number of improvements were applied to fix initial problems,
revising the standard in 1990, found in [3], as DMX512(1990).
Although the standard was initially only meant to connect dimmers to simple panels, it
has since far outgrown its original goal, interfacing mixing desks to lights, stroboscopes,
smoke machines, lasers, video servers and many more. It has also been expanded in a
supplementary protocol called RDM in 2006 which allows feedback from devices.
6
2.1.2
Communication Protocols
The standard
The DMX512(1990) standard, henceforward simply called DMX512 or DMX, describes
a data stream protocol over a balanced cable pair. Data is sent from the master or
transmitter to the slave or receiver. There can be multiple slaves, but only one master.
One DMX master can send up to 512 channels of data. The combination of these channels
is known as a frame. The DMX bus that connects the master to all slaves is called a
universe. When more than 512 channels of data are needed, additional universes (physical
DMX ports) are needed. The channel numbers continue to add up further with each new
universe.
Universe
1
2
3
4
5
Channels
1 - 512
513 - 1024
1025 - 1536
1537 - 2048
2049 - 2560
Table 2.1: DMX Channel numbering
For the slaves however, this mechanism is transparent. These devices have only knowledge
of the 512 channels in their universe.
The data stream is encapsulated in a DMX frame that is repeated continuously. Such a
frame consists of a number of start bits (to prepare and synchronise the receiver) followed
by a maximum of 512 bytes that describe each of the 512 channels. These frames can be
shorter, depending on the number of channels in use. Every channel is separated from
the next by start and stop bits.
Every time a frame is transmitted on the bus, the slaves only read the channels assigned
to them individually. It is possible for some slaves to use more than one channel, so
that advanced devices that require more than one byte of configuration data can also
be controlled, at the expense of using up more address room (channels). Devices are
configured using 9-bit Dual In-Line Package switches (DIP-switches), which can be used
to set the channel number in binary. When devices use more than one channel, these DIP
switches determine the position of the first channel.
2.1.3
Packet format
The DMX master transmits data over the bus at 250kbit/s. Therefore, every bit is 4µs
long. During normal operation, as in figure 2.1, the bus is always in any of these states:
Idle When no communication takes place, the bus will be continuously HIGH.
2.1 DMX512
7
Break DMX frames begin with a negative transition of the bus, after which the bus must
stay low for 88µs or more. Since one bit is normally 4µs, this is the equivalent of 22
low bits. This part of the frame is called break. Since the break can be longer than
88µs, it is common practice to use 100-120µs so that slaves with less-than-ideal
DMX implementations would recognise the frame.
Mark After Break This section follows immediately after the break. It is a HIGH
signal with a duration of 2 bits (or 8µs). This MAB used to be only one bit long
in the original standard, but was extended in the DMX512(1990)-standard to be 2
bits long. This was done because some receivers could otherwise not recognise the
frames correctly. For compatibility reasons, it is therefore advisable to build slaves
so that they also accept short MAB’s.
Start Code The SC follows after the MAB. This is where the timing becomes predictable
and the useful information begins. The start code and each of the channels are
always 11 pulses wide (or 44µs), including one start bit and two stop bits. The
information itself is 513 bytes, the first one being the start code, the next 512 bytes
represent the DMX channels. The pattern that is repeated 513 times looks like this:
• one LOW bit (the start bit)
• eight bits of data, representing a number between 0 and 255.
• two HIGH stop bits
In a normal DMX system, the start code data is all zeroes. It was originally meant
to group devices by function (0 for dimmers, 1 for moving heads, ...) but this never
came through and today every device uses start code 0 and ignores the frame if the
start code isn’t 0.
A DMX extension called RDM (Remote Device Management), further explained in
[4], makes use out of this by using a different start code for frames that are not
backwards compatible with regular DMX. This extension makes it possible for slave
devices to “respond” to the master.
Mark Time Between Frames The MTBF is the idle time (HIGH) between channels.
This pause is not required, but is allowed to last as long as one second. Normally
this is not used, as it slows down the information stream and decreases the refresh
rate.
Channel Data is the sequence of channels between 1 and 512 (or less) that follows after
the start code, as shown in figure 2.1. It is important to note that the channel
numbers are not included in the frame; it is up to the receivers to count the number
of channels that pass by and interpret only their assigned channel numbers. This
means that their counter needs to be incremented after receiving two stop bits and
reset when a Break signal is detected on the bus.
Mark Time Between Packets After sending a full frame it is not required for the
master to stop and idle before starting the Break and MAB of the next frame, but
this pause (called the MTBP) is allowed. The MTBP is a HIGH signal that can
8
Communication Protocols
MTBF
MAB
8 DATA BITS
STOP BITS
MTBP
IDLE
BREAK
START BIT
STOP BITS
START BIT
OTHER CHANNELS
DATA=0
START CODE
CHANNEL 1
Figure 2.1: DMX Timing Diagram
Break
MAB
Frame Width
Start/Data/Stop bits
MTBF
MTBP
MIN
88
0
0
TYP
120
8
44
4
0
0
MAX
1000000
1000000
1000000
UNIT
µs
µs
µs
µs
µs
µs
Table 2.2: Minimal, typical and maximum timing values for DMX512
scale up to one second. In practice, this idle period will be kept as short as possible
to increase the refresh rate.
The length of the frame is thus:
T = 88µs + 12µs + 44µs + NCH · 44µs + NCH · TM T BF + TM T BP
where NCH is the number of channels. When typical (Break = 120µs, NCH = 512) and
efficient (TM T BF = 0 and TM T BP = 50µs) operation is assumed, the variables can be filled
in:
T = 120µs + 12µs + 44µs + 512 · 44µs + 512 · 0 + 0
T = 22754µs
f = 1/T = 44Hz
This means that the maximum number of times a slave in a DMX bus with 512 devices
can be refreshed is 44, well beyond what the human eye perceives to be continuous. This
is an important factor for lighting applications.
2.1 DMX512
9
Figure 2.2: Common mode voltages in twisted pair cable
2.1.4
DMX512 electrically
Electrically, DMX512 is based on the TIA/EIA-485 standard [5], also known as RS-485.
This standard dictates the use of 3 wires to send binary values (1 and 0)
• Signal wire 1: the positive wire, labelled +
• Signal wire 2: the negative wire, labelled • The Ground wire (0V)
A signal is registered as "1" when the "+" wire has a higher electric potential than the
"-" wire. A "0" is registered when the "+" wire has a lower electric potential than the
"-" wire.
It is important to note that the voltage between the signal wires determines the signal, not
the voltage between any of the wires and Ground. TIA/EIA-485 does not even mandate
the use of a Ground wire, it only requires the common-mode voltage to stay between
-7V and +12V. Furthermore TIA/EIA-485 requires the differential voltage to be above
200mV. [5] In DMX512, the Ground wire is often only used as shield.
Using a balanced (differential) electrical system such as TIA/EIA-485 has two major
advantages:
• Noise immunity: DMX512 is used in very unfriendly environments such as next to
powerful class D audio amplifiers. If differential signalling were not used, the signal
would quickly be distorted by using long cables. In EIA/TIA-485, the noise is picked
up on both signal cables in identical phase and amplitude (Common Mode), thanks
to twisted pair cable (illustrated in figure 2.2). This means there is no change in
the differential signal no matter how much noise the cable picks up.
• Attenuation: Suppose a mixing desk that sends out -5V and +5V on the signal wires
(these are common values). The signal then encounters a resistive cable. Even if
this cable attenuates the signal to just +100mV and -100mV, the voltage differential
will still be 200mV and the receiver will still recognise the bit stream.
DMX512 specifies the use of 5-pin XLR connectors, with the pins connected as shown in
2.3
10
Communication Protocols
GND (0V)
Spare Data +
Signal Data -
Spare Data -
Signal Data +
Figure 2.3: DMX512 female XLR pin-out
The DMX512-standard does not specify the use of 3-pin XLR connectors, even though
3 pin XLR connectors are not only sufficient to carry the data, but also prevalent in the
audio industry. As a result, most manufacturers fit 3-pin XLR connectors to their devices.
Figure 2.4: 5-pin XLR connectors
Every DMX device has a DMX IN and a DMX OUT port. The first device in the chain
has its DMX IN port connected to the mixing desk and its DMX out port connected
to the DMX IN port on the second device. The second device has its DMX out port
connected to the third device, and so forth. The DMX IN is a male XLR, the DMX OUT
is a female XLR connector.
According to the DMX512-standard, one transmitter can only drive up to 32 devices, due
to so-called “fan-out” limitations. In practice this depends on the TIA/EIA-485 driver,
some drivers may be able to feed more devices. When more than 32 devices are to be
connected to one universe, a booster/splitter can be used. This is a device that repeats
the signalling on its DMX IN port, thereby boosting the voltage back to nominal levels.
Since the Art-Net Node can be switched to a repeater mode, it too could be used as a
booster.
2.2 DMX Over Ethernet
11
As is the case in many bus systems, a DMX512 bus should also be terminated by a
terminator resistor of 120Ω. This is done to avoid reflections off the end of the line. A
terminator plug can be produced by fitting a 120Ω resistor inside a male XLR connector
between the + and - pins and plugging it into the DMX OUT of the last device in the
chain.
Even when using boosters, the number of individually controllable slaves is still limited
to 512, when assuming every device needs only a single channel. Everything but the
most simple devices need more, so it quickly became obvious that 512 channels were not
enough. Manufacturers began equipping their mixing desks with multiple universes to
accommodate more slaves. This of course meant more cables.
2.2
DMX Over Ethernet
In order to return all control to a single cable, a number of solutions have been developed:
• ShowNet is a proprietary protocol, developed by Strand Lighting, to send DMX
signals over IP. Although the protocol has been reverse-engineered by some other
manufacturers, the specifications remain closed. Several ShowNet-enabled devices
can be found at [6].
• Pathport was developed by Pathway Connectivity, a company that makes a business
out of building data communication solutions for lighting equipment. This protocol
is also proprietary and is thus only used on Pathway devices. A device that uses
this protocol is the DMX Manager Plus! [7].
• Sandnet is a more versatile protocol, allowing for communication with MIDI nodes
as well as DMX nodes and supporting many of the open standards as well. Sandsys,
the creating company, also took the initiative to create the open standard ACN. An
example of their hardware can be found at [8].
• MA Lighting developed MA-net as a proprietary protocol for communication between their consoles and their devices, as found in [9]. The advantage of this protocol is that it broadcasts a time signal to synchronise every DMX output, so that all
“Break” signals come at the same time. This allows for very time-precise lighting
control.
• ACN [10] (Architecture for Control Networks) is an open specification for replacing
DMX512 with IP networks. This relatively new development is aiming to produce
a much more powerful protocol, capable of driving sound systems and multimedia
projectors, as well as providing feedback (RDM) to the mixing desk. A BSD-licensed
open implementation is in the works at www.OpenACN.org.
• ESP Net [11] is an open specification for DMX over IP, created by Enttec. The
protocol is very similar to Art-Net, although much simpler.
12
Communication Protocols
• Art-Net [12] is currently the de facto standard for DMX over IP. Some manufacturers, such as ADB and High End use the protocol by default. However, many
others, such as Pathway, Sandsys, ELC and Enttec have implemented it as a second
option. Art-Net is also an open specification, making it the most compatible and
most accessible protocol.
Reviewing the options, our choice to use Art-Net was not difficult to make, since we needed
to implement an open and compatible protocol. ACN would make for a very interesting
project extension, particularly in combination with RDM [4], as it would enable device
feedback.
Devices similar to what has been set as our design goals:
• Enttec DmxEtherGate MKII [13].
• Pathway DMX Manager Plus [7].
• Kiss-Box [14].
In contrast to these devices, the device that provides the subject of this thesis should use
only software of a completely open source nature.
2.3
Art-Net
Figure 2.5 should give the reader a general idea about the way our device, the Art-Net
node, will typically interact with mixing desks and acting DMX hardware.
In this section, the default method for assigning IP addresses will be discussed, as well as
the different ways in which Art-Net devices may communicate.
Art-Net [15] is an open specification created by Artistic License Ltd. The specification
describes a protocol that operates on Layer 3 of the OSI model. The communication
operates over User Datagram Protocol (UDP) on port 6454 and by default uses networks
10.0.0.0/8 or 2.0.0.0/8.
2.3.1
Address assignment
In its default mode, a DHCP server is not used, but the IP address of each Art-Net
participant is calculated from its MAC address. This calculation is best explained by
example. Consider MAC address 00:60:B3:97:73:D6. The MAC address is 6 bytes,
and must be converted to a 4-byte IP address while minimising the chance of IP address
conflict. The first 3 bytes of the MAC address (00:60:B3) are manufacturer-specific and
unimportant in this calculation. The last 3 bytes are assigned by the manufacturer and
cannot be directly used as the last bytes of the IP address, because that could cause
conflicts if not all devices on the Art-Net network are made by the same manufacturer.
2.3 Art-Net
13
Art-Net
(Cat5 Twisted pair)
Universe 1
DMX
Art-Net capable
mixing desk
Ethernet switch
Universe 2
Art-Net Node 1
Universe 3
DMX
DMX
Art-Net Node 2
Universe 4
Universe 5
Universe 6
Figure 2.5: Example of an Art-Net network
To fix this issue, Art-Net defines its own OEM table, to be found at [12, p7-9]. Every
manufacturer is free to build Art-Net capable devices and can request an OEM code
with Artistic Licence. These codes are 2-byte numbers that can be found in the Art-Net
specification. The 2 bytes in these numbers are added to each other and then to the first
byte of the manufacturer-assigned portion of the MAC address. If we assume our OEM
is 0x0430, the MAC address 00:60:B3:97:73:D6 will become the IP address (in hex)
Byte1 = 02
Byte2 = 97 + 04 + 30
Byte3 = 73
Byte4 = D6
The first byte of the IP address is either 2 or 10, in this example network 2.0.0.0/8 is
chosen. Converted to decimal form this becomes:
2.151.203.214
2.3.2
Modes of operation
2.3.2.1
Broadcast mode
In its simplest form, Art-Net devices transmit uniform UDP packets in broadcast mode.
The RFC (Request For Comments) for UDP can be found at [16]. These UDP packets are
called ArtDmx packets and are always sent with their receiver address set to 2.255.255.255
or 10.255.255.255.
14
Communication Protocols
Figure 2.6: ArtDmx packet format
2.3 Art-Net
15
The ArtDmx packet is only sent when the DMX information on a universe changes, so it
is not repeated constantly as in DMX512. The receiving node must therefore transmit the
data from the last received frame continuously on its DMX512 port until a new ArtDmx
packet arrives.
The full format for the ArtDmx packet, as it appears on the Ethernet wires can be found
in Figure 2.6. This packet format has formed from the physical layer of the OSI model,
up until the network layer. The packet size will become necessary to calculate the needed
processing power of the Art-Net Node (in 3.1.2).
The most important parts are:
• Universe: the relevant universe number for this packet
• Sequence number: an 8-bit number that increments on each new packet to ensure
that the packets arrive in the correct order.
• Data: the full DMX512 data for the universe.
A full description of ArtDmx frames can be found in [12, p19-20]
2.3.2.2
Unicast mode
Because broadcast traffic is very network-intensive, Artistic Licence only warrants the
use of broadcast mode for less than 30 universes (for 10BaseT ethernet), to prevent the
network from becoming saturated and slow. If a setup of more than 30 universes is
contemplated, unicast mode and 100BaseT are mandatory.
Three types of Art-Net devices are defined in unicast mode:
Servers These are devices that negotiate operation of the network. They may also send
data. In practice, these are often lighting desks.
Nodes These devices send or receive data on the network. Very often they simply translate information from and to DMX universes. They can either act physically upon
the information they receive or translate it to DMX.
Media Servers are much like nodes, except that they output video or audio instead of
DMX when the server demands it.
The required modes of operation for our design do not require the Art-Net Node to operate
in Server or Media Server mode. Hence, the name Art-Net Node.
Just as in Broadcast mode, Unicast Art-Net communication still takes place over UDP,
but more types of packets are defined.
2.3.2.3
Packet types
Below are the types of packets the Art-Net Node will need to be able to send or receive.
16
Communication Protocols
ArtDmx This is the typical data packet, as described in 2.3.2.1. It is unchanged in
unicast mode, except that is no longer transmitted in broadcast mode.
ArtPoll At regular intervals and at boot, a server will send out ArtPoll frames in broadcast to discover other servers, nodes or media servers on the network.
ArtPollReply All devices on the network must reply to ArtPoll packets by sending an
ArtPollReply in broadcast. Typically, one ArtPoll packet will trigger many ArtPollReply packets after which every device on the network has complete knowledge
of the others. This is because the ArtPollReply contains all relevant information
about the sender, such as IP address, manufacturer, number of DMX ports, firmware
version, etc.
Art-Net also allows for more advances features, like reprogramming, readdressing and
reflashing of devices over the UDP protocol. Although implementation of these features
is not a critical requirement, we will evaluate if it is necessary to integrate them at a late
stage of development.
ArtAddress This packet is sent by a server. It contains reprogramming information for
nodes, for example DMX512 routing information.
ArtInput Servers can monitor network traffic and send out ArtInput packets to enable
and disable DMX512 ports on remote nodes.
ArtMacMaster On factory initialisation, this packet is used to set the node’s MAC
address.
ArtMacSlave This is the reply to an ArtMacMaster packet to acknowledge receipt.
ArtFirmwareMaster These kinds of packets are used to carry firmware data, so that
Art-Net devices may be quickly reflashed without having to remove them from their
installed locations.
ArtFirmwareReply The reply to ArtFirmwareMaster packets contains information on
the success or failure of the firmware update.
ArtIpProg allows a server to change a specific node’s IP address.
ArtIpProgReply is the response (success or failure) to an ArtIpProg packet.
Additionally, more packets are defined to enable RDM and Media Server operation, but
since Media Server operation or RDM are not planned (at least not initially), they fall
outside the scope of this study.
2.4 Conclusion
2.4
17
Conclusion
We can conclude that this chapter has given us a first glance of the work to be done. We
know the type of communication we need to support electrically, as well as the communication speeds that can be expected. Those are very important factors in the next chapter,
where we attempt to choose the correct tools based on knowledge from this chapter.
18
Communication Protocols
Chapter 3
Embedded Hardware and Software
This chapter summerizes the research we conducted into embedded systems and it concludes by the design choices made for use in the Art-Net Node.
Our company promoter already determined which hardware platform we would use,
because the design of the hardware platform is based upon his Master’s Thesis
[17]. Whether this platform meets the requirements for this project, is evaluated in
section 3.1.
The following section gives an overview of the software for the Art-Net Node Project.
The choice of the different software components is explained in section 3.2.
To finish off the chapter a general conclusion can be found at 3.3.
3.1
Embedded hardware
In order to implement the Art-Net Node, an appropriate hardware platform is needed. In
embedded design the software is tightly bound to the hardware. Therefore the right platform to build the application is essential. The embedded platform needs to have sufficient
processing power and input/output to accommodate the software implementation.
3.1.1
System on chip
A system on chip (SoC) is basically an integrated controller, with the CPU-core and all
sorts of extra hardware on a single chip. This yields some interesting advantages:
• Fewer components on the printed circuit board (PCB)
• Guaranteed compatibility of hardware
• Cheaper
• Lower Power Consumption
20
Embedded Hardware and Software
• Critical paths are kept within the chip
The hardware platform is build around the SoC, so the choice of the right chip is very
important. There are several aspects to consider when choosing an embedded SoC, all of
which we will discuss.
Performance/Complexity Determining the application that will be running on the
embedded system is a good starting point when choosing the SoC. Simple designs
require less computing power so it would not be wise to choose a powerful SoC. Even
when choosing from SoC’s in the same category, subtle differences can be important.
The specified clock speed or millions instructions per second (MIPS) alone does not
give an accurate idea of performance. Indeed the total performance also depends on
the amount of cache memory, the size of the pipeline and the different instruction
optimisations.
Cost In large production quantities the cost of the hardware becomes a very important
factor. We can divide the costs into two parts. The direct costs are the catalogue
price of the SoC and other components. Indirect costs include the price of compilers,
debuggers and integrated development environments (IDE). The life span of a chip
is another important indirect cost. When a chip is no longer in production, and
a pin compatible replacement is also not available, a complete design overhaul is
needed, resulting in increased costs. One can also factor in the designer’s experience
with a particular architecture. When the time to produce a prototype is shortened
by the previous experience of the designer the total costs of the design will drop
significantly.
Power Consumption Power consumption is an important factor in embedded systems,
especially when the embedded system is battery-powered or even RF-powered. The
Central Processing Unit (CPU) is in many cases the most power-hungry component
of a system. Therefore the choice of a power friendly CPU-core can decrease the
overall power dissipation of the SoC drastically. Also there are CPU’s capable of
sleep states. With these sleep states a CPU can retain its processing power and still
be power efficient by temporary shutting down the idle components.
Software Choosing a CPU architecture is closely tied to the availability of software.
Applications need to be built for the specific architecture, so a toolchain is needed
with cross-compiler, linker and debugger. When the project requires an OS, the
support for the CPU’s architecture by the OS is quite critical. The porting of
an OS to a new architecture is a project by itself and should be avoided when
unnecessary.
Support The available support from the manufacturer can also be an important factor
to consider. When the manufacturer provides a full environment with compilers,
debuggers, OS and file system, the time to design an application can be decreased.
Also, good documentation about the hardware of the SoC is key to building a
successful design.
3.1 Embedded hardware
21
Figure 3.1: Hardware diagram
Experience of the developer and design reuse When the project designer has had
previous experience with a certain SoC or with the OS, the time to deliver a working
product can be significantly shortened. In practice, previous experience will often
be an important criterium, whether founded or not. A finished design can always
need a critical functionality upgrade, even after being put on the market. This is
why it’s important to leave some room for expansion and not choose the minimal
specs needed for the base project. It is also possible to reuse the design for a new
project. That way, the hardware is immediately available and the designer already
has the necessary environment needed for development.
3.1.2
The Art-Net Node prototype board
Koen Buys, the owner of KB Design, decided to use his own Master’s Thesis [17] as
a reference to design the hardware. This reference was modified by him to suit the
requirements of the Art-Net Node. The diagram of this redesigned prototype is shown in
figure 3.1 and a photograph of the actual board is shown in 3.2
The design is built around the AT91RM9200 SoC. This SoC has an Advanced RISC
Machine (ARM) 920T-core which is based upon ARM9TDMI design. The specifications
of the AT91RM9200 SoC [18]:
• 180 MHz Reduced Instruction Set Computing (RISC) processor
• 16 kB Instruction and 16 kB Cache Memory
22
Embedded Hardware and Software
Figure 3.2: Art-Net node prototype board
• Low current draw (typical 25mA at core voltage)
• Multimedia Card Interface for use with e.g. SD-card
• 4 Universal Asynchronous Receiver/ Transmitter (UART) that can be used to make
4 DMX512 ports
• Debugging through Debug UART or Embedded In Circuit Emulator (ICE)
• Serial Peripheral Interface (SPI)
• Two Wire Interface (I 2 C)
• 2 Full speed Universal Serial Bus (USB) Host Interfaces (12 Mbit/s)
• 1 Full speed USB Device Interface
• Ethernet Media Access Layer (MAC) 10/100 Base-T
• 2 Three channel 16-bit timers
• Serial Synchronous Controller (SSC)
The research we did points out that this SoC is indeed a good choice for the Art-Net
Node project. All the criteria mentioned in 3.1.1 have been evaluated:
Performance The ARM 920T core is clocked at 180MHz and has a memory management unit (MMU). Its critical task is routing four DMX channels, each having a
datastream of 250kbit/s and Art-Net, which utilises ethernet with a maximum speed
of 1Mbit/s.
The speed of the ethernet communication is calculated as follows:
3.1 Embedded hardware
23
• There is a maximum of four DMX512 universes (NU N ) that need to be transmitted over ethernet simultaneously at 44Hz per universe (f ) as calculated in
2.1.3.
• These packets are encapsulated by several layers according to the OSI model
[19]. A complete packet, as it is sent, can be seen in figure 2.6.
– The Art-Net Data (SAD ) has an Art-Net Header (SAH )
– This packet is then encapsulated in RFC768 [16] User Datagram Protocol
(SU DP )
– This packet is then encapsulated in RFC791 [20] Internet Protocol (SIP )
– This packet is then encapsulated in IEEE 802.3 [21] Ethernet Protocol
(SEth )
The maximum bits sent by Art-Net when sending 4 universes with all the communication overhead included becomes:
fmax = NU N · f · (SEth + SIP + SU DP + SAH + 512 · SAD )
fmax = 4 · 44 · (208b + 160b + 96b + 88b + 512 · 8b)
fmax = 818048b/s
With some headroom, the Art-Net Node will communicate with a maximum speed of
1Mbit/s (fmax ) over ethernet and the 4 UARTs all communicate with a maximum
speed of 250kbit/s (fU ART ). The AT91RM9200 has a peripheral direct memory
access controller [18, p29] (PDC) which allows ethernet and the UARTs to transfer
data from peripherals to the memory with minimal CPU overhead. A direct memory
access (DMA) transfer with the size of 8 to 32bit from the memory to the peripheral
takes 1 master clock cycle while a 8 to 32-bit DMA transfer from the peripheral to
the memory takes 2 master clock cycles (CCDM A ). The maximum amount of clock
cycles (CCmax ) per second needed for the DMX512 and Art-Net communication
becomes:
(fmax + 4 · fU ART )
8bit
(1M bit/s + 4 · 250kbit/s)
=2·
8bit
= 500000Hz
CCmax = CCDM A ·
CCmax
CCmax
The ARM 920t core is clocked at 180MHz. This means a total of 180000000 clockcycles is available per second. Knowing that the total communication takes up
a maximum of only 500000 clockcycles, or 0.28% of the system resources, we can
conclude that the CPU has plenty of clockcycles available for other tasks.
Cost The AT91RM9200 part costs $ 16.58 as of 10/2008, when bought in batches of 100
chips. This price is acceptable for production on a semi large scale. The software
24
Embedded Hardware and Software
tools will be open source and therefore will have no cost. The chosen chip has a
Plastic Quad Flat Pack (PQFP) 208 pins package which means the PCB will have
a maximum of 6 layers which isn’t too expensive to make.
Power Consumption In our application the power consumption of the AT91RM9200
is not a critical issue. The Art-Net Node will never run on battery power. The
chip only draws a maximum of 25mA current running full-performance algorithm
at typical conditions (25◦ C operating temperature, 1,8V core voltage). Also the
AT91RM9200 has a power management controller [18] with 4 states: Normal Mode,
Idle Mode, Slow Clock Mode, Standby Mode.
Software The ARM 920t core is based on the armv4t architecture [22]. This architecture
has been included in the official Gnu Compiler Collection (GCC) [23] which means
an open source compiler, linker and debugger are available. A number of proprietary
armv4t compilers are available too, such as the Keil RealView Compiler [24]. The
aim of the Art-Net Node is that the software package is completely open-source, so
using GCC is the evident choice. GCC is able to build an entire Linux based kernel
and filesystem for a AT91RM9200 based design. This makes the AT91RM9200,
GCC and Linux an attractive, flexible package.
Support Atmel gives out an extensive datasheet and application notes for the AT91RM9200.
Also, some tools and software are available for the designer, like In System Programming (ISP) and Joint Test Action Group (JTAG) ICE emulator. A prebuilt
toolchain is not maintained on the Atmel site but can easily be obtained from other
organisations [25]. Additionally, a lot of third party support can be found on the
internet. Newsgroups like Comp.Arch.Embedded have some knowledgeable developers and the AT91SAM forum [26] contains many useful posts. Timesys Linux is an
organisation which gives paying support for embedded systems, but registered as a
student we can have read only access to the site [27].
Design Reuse Because we use a redesign of the PCB made by KB Design the hardware
design takes up a lot less time. Maarten Reekmans, one of the thesis students,
did the software development of a newly created AT91RM9200 board [28] for his
bachelor final project. The experience gained in this project can be applied to the
embedded software design of the Art-Net Node.
3.1.3
Input and output
The Art-Net Node has four DMX512 ports. These ports use RS485 levels, so the UART
ports are connected to RS485 level shifters. The Art-Net protocol uses an ethernet interface which also needed to be included in the Art-Net hardware platform. Some general
purpose input output (GPIO) is also needed for user interaction, like buttons or an alphanumerical Liquid Crystal Display (LCD).
The chosen AT91RM9200 has a MAC interface for ethernet. This MAC is connected to
an ethernet PHY. This ethernet PHY is in turn connected to an external oscillator and
a pulse transformer. This transformer is then connected to the RJ45 connector as shown
in figure 3.3.
3.1 Embedded hardware
25
Figure 3.3: Ethernet Connection Diagram
[29]
The actual Art-Net hardware platform contains the following input/output:
• DP83848 ethernet PHY, pulse transformer, 50MHz clock and RJ45 [30]
• Four RS485 level shifters [31]
• USB connector, USB transient protector [32]
• Expansion header with GPIO and SPI [33]
3.1.4
Memory
Memory is always needed in an embedded system to store functional code as well as data.
Two types of memory are necessary: random access memory (RAM) to store code for
execution and non-volatile memory for firmware and data storage.
The Art-Net Node board has several ways to store data. An 8MB non-volatile serial flash
chip, two 256MB SDRAM chips and an SD/MMC card interface which gives the option
of external storage. For now, the Dataflash is used for storing the primary bootloader,
the secondary bootloader, the environment variables for the bootloader and the kernel of
the OS. An inserted SD-card is used to store the filesystem.
The memory hardware of the Art Net Node prototype board summed up:
• AT45DB642D 8MB Serial Dataflash [34]
• 2x MT48LC16M16A2P-7E 256MB Synchronous Data RAM [35]
• SD/MMC card interface for data storage
26
Embedded Hardware and Software
3.1.5
Power supply
An external power supply to convert the voltage from 230V AC to 8-12V DC is needed.
On the board itself this voltage is then regulated further throughout the board in 3 stages:
• Voltage for the peripherals: LM1084, 5V voltage regulator [36]
• Voltage for chip I/O: LM1587, 3.3V voltage regulator [37]
• Voltage for the CPU core: REG1117, 1.8V voltage regulator [38]
3.2
Embedded software
Embedded software design differs in some important ways from traditional software design. In traditional software, layers of abstraction are implemented so that the programmer doesn’t come into direct contact with hardware or low level system calls. In
embedded software, where software runs on specialised hardware, the software needs to
run as efficient as possible. This often means using lower level languages, like Assembler
or C. These languages, when programmed smart and with insight into the hardware, can
become powerful tools to get the most out of limited hardware.
The Art-Net Node requires a lot of functionality and hardware drivers to fulfil its tasks.
All this code could be executed by the CPU, without the use of an OS. This method
of working would be best for simple applications, but the Art-Net Node requires some
complex functions. Programming these functions would be time consuming and quickly
become very complex. The use of an embedded OS is therefore a good choice.
Some reasons why the Art-Net Node should use an embedded OS:
• Networking functionality: Art-Net uses the UDP protocol which is included in many
embedded OS’es.
• Applications: Generally, in an embedded OS, many useful applications are available,
like a web server.
• Hardware drivers: The drivers to initialise and operate the embedded hardware are
ported to many embedded OS’es.
• Libraries: Useful libraries like LibArtNet can be used.
• Scheduling: The scheduler of the OS can assign the proper time to any process.
• Multitasking: Several threads can be run safely together, managed by the embedded
OS’ kernel.
We should note that all of these advantages come at a price. An OS takes up a considerable
amount of resources to manage its different tasks. A hardware system needs to have
3.2 Embedded software
27
enough processing power, RAM and storage to run the OS and the user application at
the same time.
In the case of the Art-Net Node project we calculated in paragraph 3.1.2 that our hardware
platform has enough processing power to run its main application and still has a large
quantity of resources available for other things like the OS. The ARM920t core of our SoC
has a MMU available, which is also a requirement to run most OS. Finally, the system
has plenty of RAM available to run both the application and the OS.
So in conclusion we can say that our hardware is capable of running an OS and the
advantages far outweigh the disadvantages. Therefore, we decided that using an embedded
OS will be the best choice for this project.
3.2.1
OS type and kernel
A wide range of embedded operating systems is available. The two main competitors are
Windows-based and Unix-based systems. There are some alternatives as well but these
are lesser known and either not as well supported or only have paying support.
Some operating systems that are candidates for our project:
• VxWorks [39]: A proprietary real time embedded OS with a multitasking kernel,
memory protection and fast interrupt response with Portable Operating System
Interface (POSIX) PSE52 certification.
• Windows CE [40]: A component-based proprietary OS with a truly optimised embedded kernel. Windows CE conforms to the definition of a real-time operating
system, with a deterministic interrupt latency.
• Embedded Linux [41]: An open source OS with true preemptive multitasking (both
in user mode and kernel mode), virtual memory, shared libraries, demand loading,
memory management and threading.
• MINIX 3 [42]: An open source Unix-like OS. It is built to have a small resource
footprint and is designed to be very reliable.
• eCos [43]: An open source, royalty-free, real-time operating system intended for
embedded applications. eCos is made to be highly configurable and to deliver good
run-time performance, all with a small resource footprint.
When choosing an OS we should ask ourselves if we need real-time capabilities. A real time
operating system (RTOS) provides facilities which, if used properly, guarantee deadlines
can be met generally (soft real-time) or deterministically (hard real-time). An RTOS will
typically use specialised scheduling algorithms in order to provide the real-time developer
with the tools necessary to produce deterministic behaviour in the final system. An RTOS
is not faster by definition, rather than it can respond more quickly and predictably to an
event. Key factors in an RTOS are therefore a minimal interrupt latency and a minimal
thread switching latency.
28
Embedded Hardware and Software
The Art-Net Node can be considered a soft-realtime system. There are some deadlines to
be met. However it is not critical for the correct operation of the Art-Net Node that these
deadlines are met every time. When a deadline is not met, the operation of the DMX512
devices are merely delayed and no errors will occur. In the worst case these glitches should
be slightly visible. We calculated in section 3.1.2 that our hardware has enough resources
to run the application with processing power to spare, so we don’t foresee that delays will
occur under normal circumstances. Also ethernet does not guarantee the deterministic
delivery and UDP is connectionless, so the delivery of the Art-Net packets isn’t even
guaranteed. Therefore, we chose not to use an OS specially designed for real time use.
To evaluate which OS to use, we need to establish what we will need from the OS.
• Multitasking: The Art-Net application has several processes that need to be run
simultaneously. The switching between these tasks must be done by the OS.
• Memory Management: The OS should be able to detect and handle memory faults
caused by a process writing in memory not assigned to it.
• Network Stack: The OS must be able to communicate with other devices using
TCP/IP and UDP.
• Driver Availability: The hardware drivers of our platform are preferably included in
the OS.
• License: The company promoter has defined that the OS and all other software
needs to be open-source.
• Support: Good documentation and community or vendor support should exist for
the OS.
Considering all different factors we decided to run an OS based on a recent Linux kernel.
The ARM architecture is officially supported by the Linux kernel and GCC, and our
specific type of SoC is supported by third party patches provided by Maxim [44]. The
Linux kernel has integrated networking capabilities with an UDP and a TCP/IP stack,
the capability to multitask and it includes memory management. The large code base of
Linux is written by a strong community of developers. Lots of on-line information and
support is available on mailing lists and newsgroups and several books have been written
about embedded software development in Linux. The Linux kernel is released under the
GNU General Public License (GPL) [45] and is therefore completely free and open source.
3.3
Conclusion
After this chapter, we can conclude that all the fundamental choices (protocols, hardware,
software) for the Art-Net Node have been made. This chapter therefore concludes the
literature study. The rest of this document will concern our practical implementation and
actual work.
Chapter 4
Host Configuration
When building embedded software, the workflow differs from traditional software development. In most cases the actual development and building of the application is not
done on the embedded system itself. Using low powered embedded systems, building an
application would be too CPU intensive and would become very time consuming. It is
possible that the CPU architecture of the embedded system (target) is different from the
CPU architecture of the development workstation (host).
Since we are building an embedded system based upon a Linux kernel, the host system
is recommended to also run some kind of Linux distribution. The development could be
done using a Windows OS using the CYGWIN environment but this brings some extra
difficulties. Ubuntu 8.10 is used as the host system for the Art-Net Node development.
4.1
The toolchain
A toolchain is a set of linked development tools that are able to output a working application. Typically a toolchain consists of a compiler, linker and libraries.
To compile and link an application for an embedded target on a host workstation with a
different CPU architecture, a cross-toolchain is needed. The applications for the target
system are cross-compiled on the host system. The resulting application can only run on
the target system and not on the host system itself. The principal of this workflow is
shown in figure 4.1.
For the Art-Net Node we will be using a GNU toolchain for ARM, which consists of the
following components:
Gnu Compiler Collection GCC is a free, open source compiler collection which has
a widespread use in Linux environments. The architecture of the processor of the
Art-Net Node is ARM, with the armv4t instruction set. This instruction set is fully
supported by GCC.
Gnu Binary Utils Binutils is a collection of tools to perform actions on object files.
The object files linker is a part of Binutils.
30
Host Configuration
Host System ( x86)
Target System (ARM)
Figure 4.1: Workflow Cross-Compilation
Gnu Debugger GDB is used to debug applications during runtime. GDB is explained
more elaborately in section 4.2.
While manually building the toolchain for the host system is certainly possible, it is a
time consuming and tedious job. For that reason a company called Codesourcery makes
an installer package for toolchains [46]. The lite versions of these toolchains are free,
and can be easily installed [47]. Another advantage of using a pre-built toolchain is that
with every release of a new toolchain, a list of known bugs is released. Also, the user
community who all use the same version, can give better feedback when running into
problems or errors. The toolchain version we used for the Art-Net Node is arm-2008q341-arm-none-linux-gnueabi.
4.1.1
Setting the environment
Before using a cross-toolchain, some environment variables for the OS of the host system
need to be set. These variables are some global parameters for a system.
A well known example of an environment variable is the execution path. This variable
contains all the locations where the OS looks for executable files. Setting any environment
variable can be done by using the export command. The OS environment variables that
need to be set before building an application for the target are:
• PATH: contains the location of executable files and needs to include the location of
the toolchain.
• CC: points to the default compiler being used and needs to be pointing to the ARM
cross compiler.
4.2 Debugging
31
• ARCH: indicates which architecture is being compiled for and needs to be ARM.
• CROSS_COMPILE: points to the rest of the tools needed for cross compilation,
like binutils and needs to point to the location of the toolchain.
Because all these variables need to be set before every crosscompilation is done, it is useful
to include these in a script. The script also includes an alias for make. Using this alias
there can’t be any confusion about wether or not we are crosscompiling.
1
2
3
4
# !/ bin / bash alias
crossmake = ’ CC = arm - none - linux - gnueabi - gcc make
CROSS_COMPILE = arm - none - linux - gnueabi - - march = armv4t ’ export
ARCH = ’ arm ’
export PATH = $PATH :/ usr / CodeSourcery / bin /
4.2
Debugging
Debugging an application is an important aspect in any software development. Locating
and solving bugs are often a lengthy and difficult task. The most commonly used method
of debugging is still programming well placed print statements in a program. In addition
debugging can become easier when a programming language has some tools, like debuggers
available to the programmer.
Debugging software tools are applications that can monitor the execution of a program,
stop and restart a program, set breakpoints and can even change values in memory.
Because the code for the Art-Net Node is mostly written in C and the OS is Linux based,
the best debugger available is GNU Debugger.
4.2.1
GNU Debugger
GNU Project Debugger (GDB) is a portable debugger that works for many languages
like Fortran, FreeBASIC, Ada, C and C++. GDB allows you to examine an application
during runtime and it can run on most Linux and Microsoft Windows OS. For debugging,
GDB offers four ways to find bugs [48]:
• Start your program, specifying anything that might affect its behaviour.
• Stop the program on a specific condition.
• When the program has stopped, examine what has happened.
• Experiment with code behaviour by manually changing variable values.
32
Host Configuration
Target System (ARM)
Host System ( x86)
Network
connection
Application in
DEBUG mode
User Output
User Input
Figure 4.2: Workflow Remote Debugging
The programs being debugged can be executed on the same machine as GDB (native)
or on another machine (remote). Remote debugging is often much less resource-intensive
since only a small fraction of the debugging code needs to run on the target. GDB server,
running on the target, only implements the low-level functionality of the debugger which
consists of setting breakpoints, accessing the processor registers and reading and writing
application memory. The principal of remote cross debugging with GDB can be seen in
figure 4.2.
GDB server, the GDB part that needs to run on the ARM hardware, needs to be crosscompiled with the toolchain and then the executable needs to be copied to the Art-Net
Node. The application that is being debugged has to be compiled with debug information.
This can be done by providing the -g command line option to the compiler. Now we can
start debugging.
4.2.1.1
Remote debugging: target
Launch the application, compiled with debug information with GDB server. The host IP
addres and the port needs to be specified for listening to an incoming TCP connection.
$ gdbserver HOST:PORT APPLICATION [ARGS ...]
4.2 Debugging
4.2.1.2
33
Remote debugging: host
On the host, we need to connect to the listening GDB server and start the GDB session.
Next a breakpoint is set on the main function and the application runs until it reaches
this breakpoint.
target remote HOST:PORT
break main
continue
Many other commands are available in GDB. Examples are:
• s: Step through the application
• p [variable]: Print variables
• p [variable]=[value]: Setting variables
• bt: Print the backtrace
4.2.2
Boundary scan (JTAG)
Boundary scan testing is a scanning and debug testing technique devised in the late
1980’s by the Joint Test Action Group (JTAG). The JTAG committee was formed by test
professionals within Philips, BT, GEC and others. By 1990 the embedded test standard
for digital IC’s (IEEE 1149.1) was approved [49].
By embedding test cells on the boundary of each integrated circuit (IC), test patterns can
be scanned into and out of the IC by using dedicated test pins. Two additional pins are
used to control the selection of the internal registers and the flow of serial bit streams
through the parts who are daisy chained on the target test board. An example of such a
daisy chain can be seen in figure 4.3.
The boundary scan capability is achieved by adding additional logic to each device. This
logic includes scanning of registers for each of the signal pins, a dedicated scan path
connecting these registers and some control circuitry.
The JTAG port to connect the IC to the test interface has 5 pins:
1. TDI: Test Data In
2. TDO: Test Data Out
3. TCK: Test Clock
4. TMS: Test Mode Select
5. TRST: Test Reset (optional).
34
Host Configuration
TMS
TCK
Target System
TDI
TMS
TMS
TMS
TCK
TCK
TCK
TDI
TDO
TDI
TDO
TDI
TDO
TDO
Figure 4.3: JTAG Chain
Figure 4.4: USB JTAG debugger
To make use of boundary scan debugging a software tool is needed for the host. The
Open On-Chip Debugger (OpenOCD) software is an open source, free software package
that provides debugging, in-system-programming and boundary-scan testing for embedded target devices. OpenOCD uses a hardware interface dongle to communicate with the
JTAG (IEEE 1149.1) compliant taps on the target board. An example of such a hardware
interface dongle can be seen in figure 4.4.
4.3
Conclusion
In this chapter we described the methods we used to produce and debug code for our
embedded platform. Using this knowledge, we can compile and use existing code like the
Linux kernel and the GNU userland. We can also write, compile and debug our own code
while only using open-source software.
Chapter 5
Platform configuration
In order to get the main Art-Net application to run on the target hardware, the lower
level system software needs to be up and running. In this chapter we will discuss the
different steps that need to be made on the embedded system in order for it to be ready
to run the main application.
On powering up, the first level of functionality of an embedded platform is the initialisation of the hardware. The low level systems become active so that the embedded
platform’s primary functions can be executed. This first level will run through several
layers of initialisations, starting from very basic operations with limited memory and peripherals to the fully booted embedded system. The programs running the initialisations
are called bootloaders and in our platform we run through 3 such bootloaders: the on-chip
bootloader, the primary bootloader and the secondary bootloader after which the kernel
and OS can be loaded. The boot process can be viewed in figure 5.1
5.1
On chip bootloader
On powering up, the system’s registers and RAM memory are empty. The SoC provides
a piece of software stored into the ROM memory, to check if a new application is going to
be uploaded to the non-volatile memory or check if a valid application is already present
in the non-volatile memory which is then executed [17].
The on chip bootloader is capable of downloading an application in an AT91RM9200based system. It checks the external memory for a valid program or else initiates an
uploader. To evaluate if a valid program is present, the on chip bootloader looks for a
sequence of 8 valid ARM exception vectors in the following devices [22]:
• Dataflash connected to SPI
• EEPROM connected to the two wire interface (TWI)
• 8-bit memory device connected to the external bus interface (EBI)
36
Platform configuration
System Running
First Stage
Second Stage
Third Stage
System
Power On
Figure 5.1: Stages of the boot process
The ARM vectors are valid when they contain either Bbranch or LDR instructions with
the exception of the 6th instruction. The 6th instruction is used to store information such
as the size of the image and the type of DataFlash device. If a valid sequence is found,
code is downloaded into the internal SRAM.
If no valid ARM vector sequence is found in the external memory, a boot uploader is
started. This function is used to upload an application using the USB and the Device
Firmware Upgrade (DFU) protocol, or the Debug Unit Serial Port (DBGU) and the XMODEM protocol. After the upload is complete the uploader branches to the application
entry point at the first address of the SRAM.
The flowchart of the boot sequence can be seen in figure 5.2.
5.2
Primary bootloader
The internal SRAM of the AT91RM9200 is only 16KB. This means that the application
which will be executed after a valid ARM vector is found, will need to have a very small
footprint. This is the main reason why a primary bootloader is needed. The primary
bootloader initialises the external RAM memory which is far greater in size and will
have the capacity to load and execute applications with a larger footprint. The primary
bootloader can also read, write and erase the external non-volatile memory.
5.2 Primary bootloader
Figure 5.2: Flowchart bootsequence
37
38
Platform configuration
The primary bootloader’s basic functionality is displayed in the bootmenu:
ATMEL DataFlash LOADER VER 1.05 OKT 15 2008 15:34:34
*–––––––––––––––––––––-*
DataFlash[4.24 Mhz]:AT45DB642
Nb pages: 008192
Page Size: 001056
Size=08650752 bytes
Logical address: 0xC0000000
*–––––––––––––––––––––-*
1: Program Dataflashboot.bin at ...[C0000000]
2: Program U-Boot.bin at ..........[C0008400]
3: Load UBOOT from [C0008400] to [0x21f00000]
4: Program Dataflash at ...............[addr]
5: Read Memory ........................[addr]
6: Erase Dataflash containing .........[addr]
*–––––––––––––––––––––-*
Write to Dataflash The primary bootloader has access to the external Dataflash memory. It can write a custom application or a new primary or secondary bootloader
into the Dataflash, as shown by options 1,2 and 4. The data is downloaded using
the XMODEM protocol.
Read from Dataflash Option 5 reads data from the Dataflash and displays its contents
in hexadecimal values.
Erase Dataflash Option 6 erases the entire Dataflash.
Read and execute Option 3 reads the secondary bootloader’s image and copies it to
RAM memory and branches to the entry point. Option 3 is the default action,
which will run after a timeout.
5.3
Secondary bootloader
The secondary bootloader is the last bootloader and has many high level functions. The
Art-Net Node uses a secondary bootloader named Das U-Boot. U-Boot is an open source,
cross-platform bootloader and is supervised by project leader Wolfgang Denk [50]. U-Boot
provides out-of-the-box support for hundreds of embedded boards and a wide variety of
CPUs including PowerPC, ARM, XScale, MIPS, Coldfire, NIOS, Microblaze, and x86. It
has a small footprint but still has a lot of features. U-Boot is licensed under GPL and is
being actively developed by a large user community.
5.3 Secondary bootloader
5.3.1
39
Configuration of U-Boot
Because U-Boot is a generic bootloader it needs to be compiled specificly for our architecture and with support for the Art-Net Node’s hardware. The custom board designed
by KB Design has some similarities with the official Atmel AT91RM9200DK evaluation
board. By starting a new configuration file, AT91RM9200AN, based on the configuration
file AT91RM9200DK we have a good reference.
The main differences of the AT91RM9200AN configuration and the AT91RM9200DK
configuration are 2 hardware components:
DP83848 PHY The ethernet PHY used the Art-Net Node differs from the standard
Davicom PHY used by the AT91RM9200DK. The DP83848 is not directly supported
by Das U-Boot so we have to manually include the driver in the sources and make
the right references to the driver so that the compiler can build the custom driver.
AT45DB642D Dataflash Because we use revision D of AT45 type Dataflash, the standard AT45 Dataflash driver from U-Boot cannot be used. The timings of write and
erase cycles differ from the old revision [34]. The custom AT45 driver was manually included to the U-Boot source and the correct references were made in the
configuration file.
After altering the sources of U-Boot to include our custom configuration we made a patch.
This patch can be applied to the standard U-Boot sources and the result is a U-Boot
capable of supporting our hardware. Because we defined a configuration file, the selection
of our hardware is as simple as running the following command before crosscompiling the
U-Boot source:
$ make AT91RM9200AN_config
The next step is to cross-compile the U-Boot source:
$ crossmake
The resulting binary u-boot.bin is a working secondary bootloader capable of running on
our hardware and using all peripherals.
5.3.2
Using the U-Boot bootloader
Now that a U-Boot image supporting our custom hardware is available, the primary
bootloader can be used to upload the image to non-volatile memory using the XMODEM
protocol. After the image has been successfully written to the memory, the default option
of the primary bootloader will be starting U-Boot.
When U-Boot is started, it will initialise some peripherals that are needed:
• External Dataflash for non-volatile storage
• External RAM memory
40
Platform configuration
• Ethernet PHY for network functionality
• Multi Media Card (MMC) interface for data transfers to and from SD-cards
Das U-boot has a Command Line Interface (CLI) for user interaction. The CLI gives us
the ability to execute commands needed for the configuration of the boot process. The
most important commands are:
printenv/setenv/saveenv Das U-Boot can store variables into non-volatile memory.
The printenv command prints these environment variables, with the setenv command the user can set these variables and with the saveenv command the variables
are written to the non-volatile memory and thus be made persistent. Some key
environment variables are:
• ethadr: The MAC address of the ethernet PHY.
• ipaddr: The IP address of the platform (can be automatically set by DHCP).
• baudrate: The baudrate for the serial communications terminal.
• bootargs: The boot arguments to pass on to the OS with information needed
to boot correctly.
• bootcmd: The boot command that Das U-Boot executes when starting up.
cp.b The binary copy command is used to copy data from one memory location to another, it can be used to copy a file from RAM to non-volatile memory and vice
versa.
tftp The tftp command looks for a valid Trivial File Transfer Protocol (TFTP) server to
download data from. Tftp, which uses ethernet as physical layer, is the fastest way
to download a data file.
loadb The load binary command starts an XMODEM transfer to download data to the
RAM memory.
bootm The boot command looks for a valid boot image and jumps to the entry point of
that image.
With this command set available it is possible to configure the system so that the next
stage of the boot process, the loading of the OS kernel, can be done automatically. The
correct kernel image must be uploaded and copied to the non-volatile memory by either
using Network File System (NFS), TFTP or XMODEM.
5.4
OS kernel
A kernel is the low level core of the operating system. The kernel forms a layer between
the hardware platform and the user space libraries and applications. The kernel space
5.4 OS kernel
41
User Application
GNU C Library (glibc)
GNU/Linux
System Call Interface
Kernel
Architecture Dependant Kernel Code
Hardware Platform
Figure 5.3: Linux Architecture
can be accessed by user space through system calls. The general architecture of Linux
can be viewed in figure 5.3.
The kernel consists of many subsystems which perform the basic tasks:
• Memory management
• Process management
• Device management
• System Calls
As discussed in chapter 3.2, the Art-Net Node will run a Linux kernel. We chose to use
the most recent stable release of the kernel, which at this time is version 2.6.27.
5.4.1
Configuration of the Linux kernel
The stock kernel, downloaded from kernel.org [51], has no support for the AT91 range
of SoC. Maxim, a third party supplier releases a patchset for each version of the kernel
to add support for AT91 and many other ARM-based platforms [44]. After downloading
and applying this patch the kernel can be configured for the AT91RM9200DK evaluation
board, which as we discussed earlier has basically the same hardware as the Art-Net Node.
42
Platform configuration
(a) Kernel Main Menu
(b) System Type Submenu
Figure 5.4: Linux Kernel Menuconfig
Before cross-compiling the kernel, additional hardware drivers or kernel options can be
chosen using an ncurses based menu as shown in figure 5.4. Some usefull options which
we selected:
• Memory Technology Device (MTD) support for accessing the Dataflash memory
• RT73 USB Wifi support
• File Allocation Table filesystem (FAT32)
• Light Emitting Diode (LED) support
Note that at this stage, no DMX512A drivers are selected. The inclusion of the DMX
drivers will be discussed in chapter 6, because this driver needed to be manually added.
5.4.2
Building and booting the kernel
After the configuration of the kernel, the kernel can be built using the ARM toolchain
discussed in section 4.1. The right environment variables need to be set with the setenv.sh
script. In the CLI terminal of the host we can now start the cross-compilation.
First we need to compile the Linux kernel source files, and link them into a kernel image:
5.5 Linux distribution
43
$ crossmake vmlinux
The resulting image is not yet ready to be uploaded. The resulting objects need to be
converted into a binary format and some sections need to be removed.
$ arm-none-linux-gnueabi-objcopy -O binary -R .note -R .comment
-S vmlinux linux.bin
The resulting binary image, is now compressed using the gzip tool with maximum compression.
$ gzip -9 -c linux.bin > linux.bin.gz
The final step is to add a header to the file containing the load and execute address for
the image.
$ mkimage -A arm -O linux -T kernel -C gzip -a 0x20008000 -e 0x20008000
-n "Linux Kernel Image" -d linux.bin.gz AN.img
After these steps the kernel image is ready to be uploaded to the non-volatile memory on
the Art-Net Node. The fastest way to upload this file is by using tftp.
When developing the Art-Net Node, we needed to recompile and upload many kernels,
especially when integrating the DMX driver into the kernel. To speed up this repetitive
process we wrote an automation script which executes all the steps discussed in this
section and then starts a tftp server. Using the script, a new kernel could be up and
running within minutes. The script can be found in the appendix (chapter C).
5.5
Linux distribution
A Linux distribution, or distro in short, is a UNIX-like software package built on top
of the Linux kernel. A distribution consists of a large collection of applications such as
database applications, text editors, webservers, mediaplayers, ... Over 300 Linux distributions are available to date ranging from full desktop environments to compact embedded
distributions optimised for size.
In the boot process, the distribution with its root filesystem is mounted after the booting
of the kernel. The root filesystem of the Art-Net Node is stored on an SD-card. The task
of the distribution is to start all necessary services and run the init scripts. The result is
a login prompt where a user can login to.
Generally speaking there are 2 ways to approach the installation of a distribution for a
system:
44
Platform configuration
Compile from source The distribution and with it all the applications will be compiled
from source.
The advantages:
• In principal every CPU architecture is supported.
• CPU optimisations can be used which results in increased execution speeds.
• More control over the size of the applications, only the needed components are
compiled.
The disadvantages:
• The compilation process takes up a significant amount of time.
• Manually compiling is very error prone, no automatic check for build dependencies is done.
Some examples of these type of distributions: Linux from scratch with BusyBox,
Gentoo, Buildroot.
Pre-built distro The pre-compiled sources are downloaded and installed.
The advantages:
• Fast and easy installation, automatic checking of dependencies.
• More easy to maintain and update.
• Large userbase who use the same pre-compiled packages.
The disadvantages:
• Less CPU optimisations.
• Generally larger in size.
Some examples of these type of distributions: Debian, RedHat, OpenSUSE.
In the Art-Net Node project we tried one of each approach.
5.5.1
Buildroot
Firstly we used the Buildroot environment. Buildroot is essentially a set of Makefiles
and patches that make it easy to generate a root filesystem for a Linux system using the
uClibC C library.
After downloading the sources the configuration can be done using an ncurses based menu
much like the kernel menuconfig as can be seen in figures 5.5. In this menu the user is
able to select which software packages are needed and which hardware the software needs
to be built for. After the configuration the build process is started and the following
components are built:
• Cross-toolchain needed for building the applications for the target system.
5.5 Linux distribution
45
• Root filesystem with all selected applications.
• The U-Boot bootloader for the target system.
5.5.2
Debian GNU/Linux
Debian is known as a full desktop environment distribution. It has a good package management system called apt, which allows for easy installation of applications. The latest
Debian release 5.0 code named Lenny has approximately 25113 software packages available.
The main reason to choose Debian as a distribution for the Art-Net Node is because
the ARM architecture is one among twelve supported architectures. This means that
all ported applications (over 25.000 at the time of this writing) are also available for
ARM and can be easily installed on the system. When installing Debian on an embedded
system it is possible to select a version of Debian which doesn’t include the graphical user
interface.
The installation of Debian on an embedded system requires some alternative procedures
as opposed to a native installation. The first part of the installation is done on the
host system where the machine independent installation is done. The second part of
the installation is done on the actual target system. This 2 stage installation is called
deboostrapping and is performed by the deboostrap application.
First Stage The debootstrap command to be performed on the host system:
$ deboostrap –arch arm –foreign –verbose lenny .
http://ftp.de.debian.org/debian/
arch arm The ARM version of Debian will be installed.
foreign Indicate that the host and target are not the same machines.
verbose Print out extra information during installation.
lenny The version of Debian being installed.
http://ftp.de.debian.org/debian/ The mirror of Debian where the packages
will be downloaded from.
Second Stage The second stage of debootstrap on the target system will configure the
root filesystem and all necessary applications.
$ ./debootstrap/debootstrap –second-stage
5.5.3
Distribution on the Art-Net Node
After evaluating the two distributions we decided that we would proceed by using Debian.
The main reasons are the ease of use of Debian and the many applications available.
46
Platform configuration
(a) Buildroot Main Menu
(b) System Type Submenu
Figure 5.5: Buildroot Menuconfig
5.6 Conclusion
47
Additionally Buildroot uses uClibC C-library which is somewhat lesser used as opposed
to the standard GlibC C-library.
5.6
Conclusion
After this chapter, we can conclude that we have a working embedded computer that has
several important positive factors:
1. The embedded platform can easily access LAN networks due to its operating system.
2. C libraries exist for this operating system that support the use of the Art-Net
protocol [52].
3. It has a flexible, open-source kernel that allows the integration of a DMX driver
[53].
48
Platform configuration
Chapter 6
DMX Driver Integration
Thus far, we have created a working hardware platform with a recent Linux kernel and a
Debian userland. The kernel however, is not able to communicate over a DMX512 bus.
This chapter describes the steps we took to create a DMX-aware kernel that presents a
simple control mechanism to the Debian userland, and how this control mechanism works.
6.1
DMX driver
In the traditional UNIX mindset, everything in userspace looks like a file. This includes
sockets and I/O devices. The kernel is the only part of the software that talks directly
to the hardware and presents so-called device nodes (traditionally found in /dev) to the
userspace. These device nodes can easily be accessed in userspace applications by reading
or writing data to them as if they were regular files.
Of course, in order to make these devices nodes work, the kernel needs to understand the
I/O device it is dealing with and how to access it correctly. This part is done using the
device driver.
Thomas Langewouters graduated as Professional Bachelor at the De Nayer Institute in
2007. He looked for the best technical way to create DMX ports in Linux using an
AT91RM9200 based platform. Several approaches using existing software were attempted,
but none yielded satisfactory results. This prompted him to create his own kernel driver
as his Bachelor’s Thesis [53].
This driver creates the device nodes /dev/dmx0, /dev/dmx1, /dev/dmx2 and /dev/dmx3.
However, before we could utilise the driver, there were a number of challenges.
• This driver’s support for multiple DMX ports was barely tested.
• The code was created for an older version of Linux, which had a number of patches
applied to it for a specific development platform called CentiPad [54].
• A patch for the driver itself was never created. The driver was presented to us in
the form of a complete CentiPad kernel source.
50
DMX Driver Integration
• Documentation for the userspace interaction of the driver was very scarce.
There were also a great number of positive points for this driver:
• It was created for the Atmel AT91RM9200 SoC, identical to the one used on our
platform.
• DMA and event-driven architecture are part of the driver’s core, thus making it very
efficient. This is in part due to the utilisation of the UART hardware of the chip.
• The state-machine based operation of the code makes it easy to understand and
modify.
• Documentation of the kernel space code was very good.
6.2
Platform devices
The Linux kernel documentation states about platform devices:
Platform devices are devices that typically appear as autonomous entities in the
system. This includes legacy port-based devices and host bridges to peripheral
buses, and most controllers integrated into system-on-chip platforms. What
they usually have in common is direct addressing from a CPU bus. Rarely,
a platform_device will be connected through a segment of some other kind of
bus; but its registers will still be directly addressable.
In the Linux kernel, devices closest to the CPU, only accessible through memory addresses,
are called platform devices. These platform device are placed on a virtual platform bus.
A platform device may also be a bridge to a sub-bus, for example a USB controller, which
is a platform device that controls a non-virtual bus. This architecture makes it easy to
perform tasks like suspend-to-RAM, since the correct sequence to suspend devices can be
deduced from the hierarchy of the busses. While suspending to RAM, the USB device
will need to be turned off first, then the controller, along with the other platform devices.
Since the UARTS on the AT91RM9200 are platform devices, they are accessed through
a platform driver. Part of this thesis was to replace this standard driver with the DMX
platform driver. Work proceeded as follows:
Extraction Because no patch or actual code was available we needed to extract the
DMX driver manually from the CentiPad kernel. In doing so we learned that the
DMX driver relied on a custom GPIO driver from CentiPad which we also needed
to extract.
Integration After finishing the first step, we had the source files needed for the integration of the DMX driver into a standard kernel. Because we were using a recent
kernel (linux-2.6.27) and the CentiPad kernel was older (linux-2.6.18, with custom
patches), the integration of the driver brought some unforeseen difficulties. The
main issues were:
6.3 Theory of operation
51
done
Transmit Line
Break
Transmit Mark
After Break
done
ne
do
done
Read Data Buffer
From Userland
Transmit Data
done, some bytes were
written to the device node
Figure 6.1: Operation of the driver state machine in write mode
• Since linux-2.6.19 the AT91 UART subsystem had been rewritten, and the
registration of UART devices had been altered.
• The custom GPIO driver from CentiPad needed to be integrated.
• Some directories had name changes, so the location of some files needed to be
updated.
• Lots of code-defined addresses had name-changes.
The main problem when adding the driver is debugging errors. Whenever an error
occurs, the system locks up during boot with a so called kernel panic. A kernel
panic occurs when the kernel detects a fatal error such as a NULL pointer and
cannot continue. Needless to say these errors are among the most difficult to debug,
especially since we did not have a hardware JTAG dongle available at that time.
Patch creation After the successfull integration of the DMX driver in the standard
kernel we made a patch. This patch can be applied with a single command and
immediately results in a DMX-enabled kernel. The patch we created can be found
in the appendix (chapter C).
6.3
Theory of operation
The userspace application has access to four device nodes (character devices), as seen in
figure 6.2. When the application either reads (using read()) or writes (using write())
to the file descriptors, the driver automatically puts itself into the correct state machine
or mode of operation. Figure 6.1 depicts the simplest of both, the transmitting state
machine.
52
6.3.1
DMX Driver Integration
Transmitting
When transmitting, the driver doesn’t need to worry about synchronisation or errors. It
simply puts the correct digital signals onto the DMX bus, in accordance with the timing
diagrams. The state machine is therefore almost completely time-driven. It should be
noted that the repetitive nature of the DMX protocol is built into the driver: after
receiving data from userland, the driver continuously sends this last frame until new data
replaces it.
Practically, when sending each frame, the driver sets off by sending a line break. Since this
signal cannot be reproduced by standard UART commands, the TX pin is set high using
standard GPIO. The duration of this period is not simply measured, but accomplished by
putting the UART into loopback mode and sending a pre-defined number of bytes. Since
the data rate of the UART is set at 250Kbit/s, the duration of this loopback transfer is
determined by the number of bytes transferred. When the UART interrupt signals that
the transfer is complete, the driver recognises that as the end of the line break and takes
the TX pin out of GPIO mode. This same mechanism is used to transmit the markafter-break, but the actual data transmission (the start code and channel data) can be
transmitted with standard UART commands.
6.3.2
Receiving
When receiving, the state machine becomes more complicated, since the driver must take
into account that something might go wrong. When that happens, the state machine
should recover as soon as possible so that it is ready to receive the next frame without
delay or further frame loss. The receiver state machine is drawn in figure 6.3.
As can be expected, many possible paths lead to the Wait for line break state, for fast
recovery in the event of receiver error. In this state, analogous to transmitter operation,
the length of the received line break is measured by transmitting a series of bytes in
UART loopback mode. The next state, Wait for start code, advances the state machine
if it receives a valid start code, meaning that the first byte of actual data transmission is
zero. After that, UART DMA is used to receive the DMX channel data in the Receive
channel data state. Since a DMX frame is not required to carry 512 bytes of data (the
DMX512A specification implies that there should be at least 96 [3]), there are two possible
exits for this state. Either a line break is received, meaning that the transfer of less than
512 bytes is complete and the next frame is going to be sent, or the UART signals that
the DMA transfer of 512 bytes is done. Both cases prompt the driver to send its received
data to userland and carry on.
6.4
Simple testing
In order to test the DMX driver, we connected the RS-485 transceivers to an Agilent
memory oscilloscope and recorded the events on the bus. Figure 6.4 is a zoomed-out view
of a DMX frame. It shows the bus in its idle position, logically “high”, followed by data
6.4 Simple testing
53
Kernel Space
User Space
poll()
Device Nodes
Figure 6.2: Function and situation of the character devices
Framing error
Timeout
Line Break
Wait for Start Code
t
End of UART DMA
Line Break
cod
e re
ceiv
ed
star
t
Send data to
userland
Vali
d
ou
time
r or
A
T DM
erro
ing
Fram
AR
of U
End
Invalid start code received
Line
Br e
ak
Wait for Line Break
Read Frame Data
Figure 6.3: Operation of the driver state machine in read mode
54
DMX Driver Integration
Figure 6.4: Zoomed-out view of a DMX frame as sent by the driver
Figure 6.5: Detailed view of the beginning of an all-zero frame as sent by the driver
transmission approximately 25ms in length. So, at first glance already, the frame looks
valid.
Figure 6.5 takes a closer look at the beginning of the frame. This immediately reveals
the “line break” and the “mark after break”, about 130µs and 75µs in length respectively.
Comparing all the timing values to the official DMX512 specification, we can conclude
that the frame is indeed valid.
To see if the data sent to the character device is actually transmitted across the bus, we
sent a series of 512 0xAA bytes to the device using dd if=/dev/0xa of=/dev/dmx0. The
result is shown in figure 6.6. After the “mark after break”, the bus remains low for about
25µs. This is of course the Start Code, which is always zero in classic DMX512A. This
frame also allows measurement of the length of one bit, which we found to be exactly 4µs,
resulting in a correct speed for DMX512 of 250Kbit/s.
6.5
Advanced testing
Although simple measurements confirmed the driver’s valid basic code, they couldn’t
guarantee fast and reliable operation in more realistic scenarios. This is why we wrote
6.5 Advanced testing
55
Figure 6.6: Detailed view of the beginning of a 0xa frame as sent by the driver
“stub” programmes to test its real-world capabilities. By connecting the DMX I/O of
the Art-Net Node to third party applications, we were able to check for any faults in the
implementation of the driver, as well as stress-test the entire system. The third party
hardware we had at our disposal was:
• An Enttec DMX USB PRO dongle [55].
• A Showtec LED PAR 56 Short DMX-controlled stage light [56].
After some work, we were successfull in all of our tests and were able to reliably communicate over a DMX bus at maximum refresh rate (44Hz). The tests we conducted can be
found below:
Driver as output The driver is used as in figure 6.1.
• Controlling the stage light from the userspace application.
• Transmitting data over the DMX bus to the Enttec USB dongle while checking
consistency.
Driver as input The driver is used as in figure 6.3.
• Receiving data over the DMX bus from the Enttec USB dongle while checking
consistency.
Since the userspace documentation of the driver was scarce, a different aspect of these
test was the discovery of how to interact with the kernel driver. We learned the sequence
of system calls this driver expects for correct operation.
open() By opening the device node file the driver remains in idle state, but is ready to
either send or receive data. To ensure correct and fast operation, O_NOCTTY and
O_NONBLOCK are mandatory arguments to the open() function.
56
DMX Driver Integration
write() After using this system call to write data (with a maximum of 512 bytes) to
the character device, the driver begins transmitting these bytes repetitively to the
DMX port.
read() After using this system call, the driver listens for data on the DMX port. Typically, the first time this call is executed, no data will be returned, since O_NONBLOCK
doesn’t allow for the application to wait until data has been received. The first
execution will therefore merely put the driver in receiving mode, the second time
will typically receive data, as will be explained by poll().
poll() This call is the most important one. One of its arguments is a series of four file
descriptors, one for each DMX port. The system call waits until an event occurs
on any of the files, indicating that a specific DMX port is ready to send or receive
more data. As its second argument, a timeout can be set so that execution of the
application is not blocked longer than permitted by the design. It is important to
note that execution of this call is required for correct operation of the driver, since
the driver state machine “waits” for it.
close() As mentioned before, in transmitter mode, DMX data is sent out in a repeated
fashion. This process can be interrupted by a read call, but that will also configure
the driver for receiving. To simply stop the sender process, closing the file descriptor
will put the driver back into idle mode. Analogously, receiver operation can be halted
by close() as well.
The stub programmes can be found in the appendix (chapter C).
6.6
Conclusion
Again, we are one step closer to the working prototype of the Art-Net node. Our embedded
computer can now send out and receive the DMX512A-protocol. Furthermore, it does
this in the traditional UNIX way, which means:
1. The kernel handles the device drivers for optimal speed.
2. Character devices are used to control the driver from user space.
We have also tested this driver using stub programmes, yielding promising results. The
inputs and outputs of the Art-Net node are therefore ready. So far, only the routing
operation remains to be implemented.
Chapter 7
Art-Net Router Software
The previous chapters have explained in detail the building blocks of the Art-Net node.
The foundation for routing operation in four different modes, as described in chapter 1,
has been laid. We now have an embedded system with an operating system that on one
side can access IP networks. On the other side, it can send and receive DMX on its four
independent ports. We also have a library that can control the behaviour of the device in
the Art-Net network. In this chapter we will link all of these building blocks together by
creating the userland router application.
The chapter starts off by clarifying some of the design choices in 7.1.
The application will only take up a single process identity (PID), but run in two threads:
the worker-thread, explained in 7.4 and the controller-thread, explained in 7.2.
These threads communicate with each other over a protocol, defined in 7.3
To sum up the possibilities of this design, we close off the chapter with a conclusion at
7.5
7.1
Design basics
Since the design goal of the project includes a web interface that can show and update
the configuration (and updating the configuration should create as little interruption or
hassle as possible), we needed to design a flexible and generic way to configure the routing
table. This routing table would need to allow the pre-defined four modes of operation,
while making on-the-fly changes possible.
To accomplish this, we decided to use an I/O table, internally stored in a C-style structure
(or struct). This table has a field for each input and several output fields for each of these
input fields. To determine the size of this table, we reasoned as follows:
The Art-Net node has five I/O ports:
• Four DMX ports
58
Art-Net Router Software
• One Ethernet port
Granted, Art-Net supports the transfer of tens of universes (see 2.3). The Ethernet port
can therefore transport many times the data of the DMX ports, but this does not change
the fact that there can be only four inputs to a five I/O port routing device. In any case,
the data needs to go somewhere, so at least one of the ports needs to be used as output.
This maximum amount of input ports is used when the node is used to route 4 DMX
universes to Art-Net. This is the second mode of operation, shown in figure 1.2.
Because of this, we can conclude that the amount of input slots in the I/O table should be
four. The amount of output slots per input slot should also be four, since one input can
only route data to a maximum of 4 outputs (the other one being used by the input itself).
An example of the I/O table is given in table 7.1. In the example, Art-Net universe 8,
DMX port 1 and DMX port 4 are the inputs. Data from Art-Net universe 8 is routed to
DMX ports 3 and 2, while data from DMX port 1 is routed to Art-Net universe 2. In the
same fashion, data from DMX port 4 is routed to Art-Net universe 10.
Input
Art-Net
universe 8
DMX
port 1
DMX
port 4
N/A
N/A
Output
DMX
port 3
DMX
port 2
N/A
N/A
N/A
N/A
Art-Net universe 2
N/A
N/A
N/A
N/A
N/A
N/A
Art-Net universe 10
N/A
N/A
N/A
N/A
N/A
N/A
N/A
N/A
N/A
N/A
N/A
N/A
N/A
N/A
Table 7.1: Example I/O table
Of course, it is also possible to route DMX inputs to different ports, so the four modes
of operation discussed in chapter 1 can be all be configured using this flexible table configuration. Furthermore, this type of configuration is very easy to read from a machine’s
point of view. A pair of loops, one embedded in the other, can easily run over the table
and take appropriate action for each frame of data received. The loops we used are drawn
in figure 7.2.
7.1 Design basics
59
Worker
Controller
Mutex
No
Yes
No
Yes
Figure 7.1: Flowchart of the main application
60
Art-Net Router Software
7.2
Controller thread
As said before, the main application will run in two threads. Figure 7.1 depicts the
operation of both threads. This section discusses the thread on the right, the controller.
It has three functions:
Read configuration file At the start of execution, the controller will read a configuration file and send it to the worker thread (discussed in section 7.4). This configuration file includes an I/O table, IP address, subnet mask and many other parameters.
The data is sent between threads using a mutex, to exclude the possibility of writing
and reading to the same memory address at the same time from both threads.
Write configuration file When appropriate, a new configuration file will be written to
flash memory, to be read on the next execution of the program.
Read from web interface The third (and main) purpose of the controller thread is to
open up a socket and listen for commands originating from the web interface. These
commands will be discussed in the next section. When the configuration changes,
the thread will use the mutex again to send the data to the other thread. Then, it
will write the configuration data to flash memory.
7.3
Control protocol
We decided to use a socket or TCP port to to listen for configuration updates, so that it
would be possible to reconfigure the device in a flexible way. A listening TCP port makes
it possible to carry out a configuration change from virtually any network-enabled device
and any programming language. This was not a design goal but simply best-practice in
our opinion. In the current plans, only the web interface will connect to the socket and
send line-break -terminated strings (or commands) to the controller thread. Some of these
commands are illustrated below.
• SET IPADDR "XXX.XXX.XXX.XXX"/"YYY.YYY.YYY.YYY"
Reconfigures the IP address of the Art-Net node. XXX and YYY are 8-bit numbers
represented in decimal form (0-255), where XXX is a part of the IP address and YYY
is part of the subnet mask. This command takes effect immediately, so it will likely
require the administrator to redirect his browser to the new address after execution.
• SET SHDESC "Art-NetNode"
Sets the Short Description for the Art-Net node. This description is sent to other
Art-Net-capable devices in ArtPollReply packets, previously discussed in section
2.3. It is up to 17 characters in length.
• SET LODESCD "KB Design Art-Net Node v0.1 20090501"
Sets the long description. This is nearly identical to the short description, except
that it may be up to 63 characters in length.
7.4 Worker thread
61
• SET ROUTEI <inputnr> <input>
Configures the input slot of one I/O table element.
– inputnr is the table input row number (1-4).
– input is a port identifier and a number, concatenated. Valid examples are A30
or D3, meaning Art-Net universe 30 or DMX port 3.
• SET ROUTEO <inputnr> <outputnr> <output>
Configures any of the four available output slots of an input slot.
– inputnr is the table input row number (1-4).
– outputnr is one of the four output slots (1-4).
– output has a syntax identical to input. It describes one of the “route-to”-fields
of an I/O table element.
• CLR ROUTE <inputnr>
This command clears the input slot as well as the four output slots of a table row.
• OK
The OK-command is special because it travels in the opposite direction from the
previous commands. It is sent from the main program to the client (web interface)
to indicate that the previous command was received and executed successfully.
• NOK
As the negative version of OK, NOK indicates something was wrong with the previous command. This might be a syntax error or illegal data.
7.4
Worker thread
The worker thread, as seen on the left in figure 7.1, has a very repetitive nature. During
normal operation, it continuously loops over the the “Route DMX” and “Route Art-Net”blocks. Only when it receives an alarm signal (SIGALRM) during the cycle of operation, it
re-reads the configuration data before starting the next cycle (It also resets the alarm).
This is done to save CPU-time. Since the alarm is always set to one second, there may
be a delay of up to one second between updating the configuration on the web interface
and the actual change in behaviour of the program. Since the configuration data should
change only rarely, this is not a problem.
In figure 7.1, most of the blocks carry out a fairly straightforward function. This is with
exception of two: Route DMX and Route Art-Net. Route DMX is drawn in detail in figure
7.2. The algorithm starts by execution a poll() system call (as explained in section 6.5)
on the four DMX ports. The poll() function exits when data is pending on one of the
DMX ports. The function then loops over the ports to find out which one(s) have data
pending. If a port with pending data is found, the outputs for this port are looked up in
the I/O table and the data is routed correctly.
Route Art-Net is depicted in figure 7.3. This figure is simpler for two reasons:
62
Art-Net Router Software
No
Yes
Yes
Yes
No
No
Yes
Figure 7.2: Flowchart of the DMX routing algorithm
7.5 Conclusion
63
No
Yes
No
Yes
Figure 7.3: Flowchart of the Art-Net routing algorithm
1. libartnet has a mechanism that can pass the correct port number (and I/O table
element) to the routing function. Because of this, when data is received, the only
things that need to be looked up are the outputs.
2. Art-Net to Art-Net repeating is not useful. When data is already present on the
LAN, there is no reason to repeat it. That would even be dangerous, since it might
lead to network storms if two such repeater devices are present on the LAN.
7.5
Conclusion
The working practical implementation of the concepts we discussed in this chapter brings
the Art-Net node very close to being a finished product. The four modes of operation we
first discussed in chapter 1 can now be configured in various permutations. Live configuration updates are possible with maximum transparency, since the changes will take effect
within one second. These changes can originate from the web interface (chapter 8) but we
left open the possibility of doing this configuration directly from remote devices over the
LAN. At the time of this writing, the control protocol has not yet been implemented, so
this code cannot be found in the appendices. Preliminary code that demonstrates routing
capability in all possible configurations is included in the listings.
64
Art-Net Router Software
Chapter 8
Web interface
In the previous chapters, the hardware platform and system software was installed on
the Art-Net Node and the main routing and translating application was written. The
only thing the Art-Net Node misses before it can be truly functional is an interface for
configuration. For this purpose we are going to use a web interface. The web interface is
the Graphical User Interface (GUI) front end of the Art-Net Node. The configuration of
the Art-Net Node will be done by altering parameters on a web page. These configuration
parameters are then sent to the main application. A clear-cut view of the current routing
and network settings are then displayed on the webpage as feedback to the user.
8.1
Webserver
The webserver, a server application that runs on the Art-Net Node, will be accepting Hypertext Transfer Protocol (HTTP) requests from clients. The client, an internet browser
like Firefox or Safari can be running on a remote PC, a Personal Digital Assistant (PDA)
or even a smartphone. After the HTTP request, the webserver will present a HTTP response and serves up the requested webpage. The presented webpage will be dynamically
formed by the data present on the Art-Net Node. The overview of this workflow operation
can be seen in figure 8.2.
On the client side (the browser) a combination of interrelated web development techniques, called Asynchronous JavaScript and XML (AJAX) is used. With AJAX, web
applications can retrieve data from the server (the Art-Net Node) asynchronously using
the XMLHttpRequest object. The advantage of this asynchronous communication is that
components of a webpage like a text field or an image can be updated without refreshing the entire webpage. The difference between AJAX technology and the classic web
technology can be seen in figure 8.3.
66
Web interface
No
Yes
No
Yes
Figure 8.1: Flowchart Configuration Update (PHP/Server side)
8.2 Configuration page
67
Art-Net Node
Configuration Clients
(web browser)
WiFi Connection
Control
Protocol
Ethernet Connection
Figure 8.2: Configuration Overview
8.2
Configuration page
The webpage being served to the user is generated on the webserver using Hypertext
Preprocessor (PHP). The main tasks of the server-side PHP processing can be viewed in
figure 8.1. These main tasks are:
Input validation Before updating the configuration of the Art-Net Node, the new configuration that was entered by the user must be validated. When an invalid configuration parameter is set by the user, an error message needs to be returned and no
updates should be sent to the main application.
Updating configuration When the input has been validated the new configuration
parameters can be sent to the main application. For the communication between the
web-based processing and the actual main Art-Net application a socket connection
is set up and the new configuration is sent using the control protocol explained in
section 7.3. When the main application has successfully received a command string
an ACK is sent to confirm successful reception.
Presenting data When an ACK command has been received by the web-based processing, the new configuration is displayed on the web page and as such presented to
the user.
8.3
Conclusion
The use of a web interface means that the configuration can be remotely updated, possibly
over a wireless network. Because the communication uses a socket and a control protocol, another GUI, perhaps written in a different programming language, can be easily
developed without changing the main application.
The web interface is the final part of the core functionality of the Art-Net Node. The
Art-Net Node can now be put into a DMX/Art-Net network, where its settings can be
68
Web interface
Browser Client
Browser Client
JavaScript
call
HTTP
request
HTTP
request
HTTP Transport
HTTP Transport
XML
data
HTML+CSS
data
Server-side System
(a) Classic
Model
HTML+CSS
data
Webapplication
Server-side System
(b)
AJAX
Model
Webapplication
Figure 8.3: AJAX and Classic Webapplication Models
8.3 Conclusion
69
updated using the webinterface. Without change, the node can work independently until
reconfiguration is required.
At the time of this writing the GUI is still being developed, so the code for this GUI is
not included in the appendices.
70
Web interface
Chapter 9
Achievements
In retrospect, we can look at this Master’s Thesis and make an overview of the accomplishments that we have made in the last few months:
Host configuration: In order to build software for an embedded system we configured
a host system for cross-target development. Setting up a good working environment
with a cross-toolchain for the Art-Net Node platform is one of the first achievements
that we were able to complete successfully.
Debugging the base system: The redesigned PCB and accompanied software provided to us for the Art-Net Node project was not fully functional. A couple of
problems arose that we were able to resolve:
• The Ethernet communication did not work. After a lot of research in both software and hardware we found a hardware bug. The crystal oscillator connected
to the ethernet PHY had the wrong frequency. Because of this mismatch, the
communication between the ethernet MAC and the PHY was not working. The
error was resolved by soldering the correct crystal oscillator onto the PCB.
• The provided software for the Art-Net Node platform was unable to write to the
serial Dataflash memory of the system. By locating and configuring the correct
primary and secondary bootloaders we were able to write into the Dataflash
memory and thus provide a system that was able to boot a full OS.
• A minor bug was found when trying to test the level shifters for DMX. The
send enable pins were not connected to the I/O headers on the PCB. We also
realised that the enable pin needs to be inverted because otherwise unwanted
signals can inadvertently be transmitted during the booting of the Art-Net
Node.
Building Kernel: We successfully configured the Linux kernel to use all the hardware
and peripherals of our custom Art-Net platform. In order to do this we needed to
patch a mainline kernel with the Maxim patch [44] to enable AT91-based hardware.
When this step was complete, we built the kernel and installed it on the Art-Net
Node.
72
Achievements
Including DMX: The DMX driver was given to us integrated in a custom kernel source
tree with no information on how to integrate it in a recent mainline kernel. We
needed to extract the DMX driver source files from the given kernel and try to
integrate the source files in a recent mainline kernel. For this achievement we needed
a thorough understanding of the Linux kernel architecture, the DMX driver source
code and kernel debugging techniques. After having extracted the driver, we created
a kernel patch that easily applies to a mainline kernel and integrates the DMX driver
without hassle.
Writing the main application: Perhaps the most clear-cut achievement of this Master’s Thesis is the development of the main routing and translating application. To
write this application we needed to understand in detail how the DMX driver works
and how to interface the driver with a userspace application. For routing Art-Net,
the libartnet library is available which we needed to understand in order to interface
the library with the main application. In summary the main application is the final
piece which puts together all previous building blocks made by us as well as other
developers, and we can say that this part of the Art-Net Node is fully functional
without major bugs.
Writing the web interface: As of this writing the web interface is yet to be implemented. The architecture of the web interface and the control protocol for the
communication with the main application have both been conceptualised, but the
code still needs to be written. Because we still have some time left and we both
have a significant amount of experience writing web applications, the web interface
should be finished by the time of the demonstration of the Art-Net Node to the
members of jury.
Writing documentation: Because the software for the Art-Net Node is open source, we
tried to facilitate the work of future developers by documenting the code as good as
possible. We used in-line comments as well as a Wiki page to provide all the information needed. The MediaWiki site we set up can be found at www.artnetnode.com.
We set off early this year with the task of producing a working prototype capable of
translating and routing DMX and Art-Net. In many ways we can say that this goal has
been achieved. As with most prototypes the Art-Net Node still has some bugs and some
features have not been implemented yet. However, the most difficult and important part:
the core features, are in place and are working as specified. These core features can be
easily accessed by an interface, which means that another developer can easily pick up
the further development without much hassle. This is likely to be the most important
contribution we made to this project.
Chapter 10
Conclusion
The goal of this dissertation was not to have a proof of concept or a strictly theoretical
study of a subject. Instead its goal was to produce an actual working prototype of the
Art-Net Node. We believe we have succeeded in that aspect. However, during the work,
we realised that having a working product is only a part of a true accomplishment. We
believe our greatest contribution to this project is not the end result, but the fact that
there now exists a tried-and-tested hardware platform, a ready-to-go kernel, a functional
main application and useful documentation.
Even though the main application may not implement every possible feature or may
exhibit some bugs, above all else it sets a very clear example of how to continue the
work. Koen Buys, our company promoter, has made it clear that he intends to release
this project’s software under the GNU GPL License, making it completely free and open
source, while keeping control over the hardware designs. This would indeed be ideal
for further development. Should someone else (perhaps an external party) decide to
contribute to this project, they should be able to develop useful additions or extra features
in a very short time, since the fundamental difficulties have already been overcome.
74
Conclusion
Bibliography
[1] M.Reekmans/R.Verbiest, Art-Net Node Wiki Page, http://artnetnode.com/
wiki/index.php?title=Main_Page#Art-Net_Node, 2009.
[2] United States Institute for Theatre Technology, http://www.usitt.org/.
[3] Entertainment Services and Technology Association, American National Standard
E1.11-2004 Entertainment Technology USITT DMX512-A Asynchronous Serial Digital Data Transmission Standard for Controlling Lighting Equipment and Accessories,
cp/1998-1031r8.0 edition, 2004.
[4] Entertainment Services and Technology Association, American National Standard
E1.20 - 2006 Entertainment Technology RDM Remote Device Management Over
DMX512 Networks, cp/2003-1003r4 edition, 2006.
[5] Texas Instruments, Interface Circuits for TIA/EIA-485 - Design Notes, slla036 edition, 1998.
[6] Strand Lighting N21 ShowNet Node product page,
http://www.
strandlighting.com/index.php?submenu=Shownet&src=directory&view=
products_detail&srctype=display&refno=24&category=Controls, 2008.
[7] PathWay PathPort DMX Manager Plus!
product page,
pathwayconnect.com/content/view/68/29/, 2008.
http://www.
[8] Sandsys SandBox UMX4 product page, http://www.sandsys.com/products/umx4.
htm, 2008.
[9] MA-Lighting Network Signal Processor product page, http://www.malighting.
com/signal_processing.html?&tx_lightpowerpdb_pi1[parent_gruppe]
=223&tx_lightpowerpdb_pi1[produkt_id]=810&cHash=2180cd8c76, 2008.
[10] The OpenACN Project, http://www.engarts.eclipse.co.uk/acn/openacn.html,
2008.
[11] ENTTEC Pty/Ltd, DMX on ETHERNET Protocol, 1.4 edition, 2001.
[12] Artistic Licence, Specification for the Art-Net mx Ethernet Communication Standard,
1-4ag edition, 2004.
[13] DMX EtherGate MKII,
http://www.enttec.com/index.php?main_menu=
Products&pn=70018&show=specification, 2008.
76
BIBLIOGRAPHY
[14] Kiss-Box DMX512 tranceiver, http://www.kiss-box.com/, 2008.
[15] Artistic Licence Website, http://www.artisticlicence.com.
[16] J. Postel, RFC 768, UDP Datagram Protocol, ISI, 1980.
[17] K. Buys, A design guide to embedded ARM platform building, Master’s thesis, De
Nayer Instituut, 2008.
[18] AT91RM9200 Summary (38 pages, revision K, updated 09/06), http://www.atmel.
com/dyn/resources/prod_documents/1768s.pdf, 2008.
[19] J. Meel, Datacommunicatie (deel2), 2005.
[20] Information Sciences Institute, University of Southern California, RFC791 IP Internet Protocol, 1981.
[21] Institute of Electrical and Electronics Engineers, Inc., 802.3 Ethernet Standard, 1990.
[22] AT91RM9200 (689 pages, revision G, updated 09/06), http://www.atmel.com/dyn/
resources/prod_documents/doc1768.pdf, 2008.
[23] Gnu Compiler Collection build targets, http://gcc.gnu.org/install/specific.
html, 2008.
[24] Keil Realview Compiler for armv4t,
2008.
http://www.keil.com/dd/chip/4088.htm,
[25] Atmel AT91RM9200 Product Card,
http://www.atmel.com/dyn/products/
Product_card.asp?part_id=2983, 2008.
[26] AT91 forums, http://www.at91.com/Home/Controleurs/cHome.php, 2008.
[27] LinuxLink Embedded Timesys, http://www.timesys.com/products/processors/
arm/overview/, 2008.
[28] M. Reekmans, Ontwikkeling van een embedded systeem, 2006-2007.
[29] National Semiconductors, DP83848 Product Summary, 2005.
[30] National Semiconuctors, DP83848 Ethernet PHY [Datasheet], 2007.
[31] Maxim, MAX3072E RS485level shifter [Datasheet], rev 1 edition, 2001.
[32] ST micoelectronics, USB6 1RL USB transient protector [Datasheet], rev. 9 edition,
2006.
[33] K. Buys, Art-Net Node Schematic, KBDesign, 2008.
[34] Atmel, AT45DB642D Dataflash [Datasheet], rev. h - april 2008 edition.
[35] Micron, MT48 256 SDRAM [Datasheet], rev l edition, 2007.
[36] National Semiconductors, LM1084 5V regulator [Datasheet], 2005.
BIBLIOGRAPHY
77
[37] National Semiconductors, LMS1585A 3.3V voltage regulator [Datasheet], 2005.
[38] Texas Instruments, Reg1117 1.8V Voltage Regulator [Datasheet], rev b edition, 2001.
[39] Wind River Systems, VxWorks Manual.
[40] Windows CE embedded development center, http://msdn.microsoft.com/en-us/
library/aa924098.aspx, 2008.
[41] K. Yaghmour, Building Embedded Linux Systems, Number ISBN 0-596-00222-x,
O’Reilly, 2003.
[42] Minix3 organisation, http://www.minix3.org/, 2008.
[43] A. J. Massa, Embedded Software Development with eCos, Number ISBN-10: 0-13035473-2, Prentice Hall, 2002.
[44] Maxim AT91 Linux Kernel Patches, http://maxim.org.za/at91_26.html, 2008.
[45] GNU General Public License, http://www.gnu.org/copyleft/gpl.html.
[46] Codesourcery Website, http://www.codesourcery.com/, 2009.
[47] Codesourcery Toolchain Download, http://www.codesourcery.com/sgpp/lite/
arm/portal/subscription?@template=lite, 2009.
[48] GNU Project Debugger, http://www.gnu.org/software/gdb/, 2009.
[49] IEEE Std 1149.1-1990 IEEE Standard Test Access Port and Boundary-Scan Architecture, http://standards.ieee.org/reading/ieee/std_public/description/
testtech/1149.1-1990_desc.html, 1990.
[50] W. Denk, Das U-Boot Universal Bootloader, http://www.denx.de/wiki/U-Boot,
2009.
[51] The Linux kernel archive, http://www.kernel.org, 2009.
[52] S. Newton, Libartnet - A Linux ArtNet Library, http://www.nomis52.net/
?section=projects&sect2=artnet&page=libartnet, 2009.
[53] T. Langewouters, Implementatie van een Artnet-Node op een embedded Linux
systeem, 2006-2007.
[54] CentiPad Embedded Linux Modul AT91RM9200,
centipad/index.html, 2009.
http://www.harerod.de/
[55] Enttec DMX USB PRO,
http://www.enttec.com/index.php?main_menu=
Products&pn=70304&show=description&name=dmxusbpro, 2009.
[56] Showtec LED PAR 56 Short,
artnr=51913620&lang=E, 2009.
http://www.eurolite.de/eur_artdetail.php?
78
BIBLIOGRAPHY
Appendices
Appendix A
Technical Specifications
• Codesourcery version: arm-2008q3-41-arm-none-linux-gnueabi
• Dataflashloader version: 1.05
• Das U-Boot version: 1.1.5
• Linux Kernel version: 2.6.27
• Debian GNU/Linux version: 5.0 “Lenny”
82
Technical Specifications
Appendix B
Errata
B.1
Software bugs
• Dynamic linking of the main application does not work. After examination, it turns
out that the version of ld (gnu linker) we are using, utilises the ARM blx-instruction
when jumping to a function via a function pointer. blx is supported in the ARMv5t
instruction set or later, but the linker uses it despite the -march=armv4t parameter
• For reasons yet unknown, the DMX kernel driver sometimes throws the error Resource
temporarily unavailable while writing data to a DMX char device. This is despite the use of the poll() system call before writing.
B.2
Hardware bugs
The protoype board is a reference design for the finalized product. For the actual production a revision of this board will probably be used. Some bugs were found during the
development which need to be addressed:
Parallel flash memory The serial dataflash is limited in size, so the filesystem of the
OS needs to be on another storage device. For development an SD-card is good, but
for a production model the filesystem needs to be on a non-removable component.
Generally parallel flash memory is available in greater capacities than serial flash.
SDRAM : On the prototype board 512MB of RAM is used. This is too much for the
ARM9 to address and is not useful. Lower capacity RAM will be much cheaper and
will bring the overall production cost down.
Voltage regulation The voltage regulators on the protoype board generate quite a lot
of heat and require a heatsink. The heatsink adds costs and makes the assembly of
the production model more difficult. Energy efficient switching voltage regulators
don’t produce as much heat and are ideal for the production board.
84
Errata
Debug RTS pins The AT91RM9200 pins that connect to the RS-485 transceiver as
transceiver enable signals, as shown on the diagram, are also connected to a GPIO
header for debugging. However, in reality, this is not the case: these pins are only
connected to the RS-485 transceivers.
Appendix C
Code Listings
Prepkernel script:
#! / b i n / b a s h
2 #W r i tt e n by MR
4 UBOOTTOOLS=/home/ maarten / Art−Net / B u i l d / Debian / debian_uboot / 1 . 1 . 5 / u−boot − 1 . 1 . 5ULFZELF/
tools /
6 echo " Making K e r n e l r e a d y f o r u pl oa d : "
echo −n " ∗ O b j e c t c o p y . . . "
8 arm−none−l i n u x −gnueabi−o b j c o p y −O b i n a r y −R . n o t e −R . comment −S vmlinux l i n u x . b i n
echo "Done"
10 echo −n " ∗ Compressing . . . "
g z i p −9 −c l i n u x . b i n > l i n u x . b i n . gz
12 echo "Done"
echo " ∗Make image h e a d e r . . . Done "
14 echo " Image i n f o r m a t i o n : "
echo "−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−"
16 i f [ −n " $1 " ] ;
then
$UBOOTTOOLS/mkimage −A arm −O l i n u x −T k e r n e l −C g z i p −a 0 x20008000 −e 0 x20008000 −n "
Linux K e r n e l Image " −d l i n u x . b i n . gz $1
18 echo "−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−"
echo −n " ∗ C r e a t i o n o f Image $1 . . . "
20 e l s e
$UBOOTTOOLS/mkimage −A arm −O l i n u x −T k e r n e l −C g z i p −a 0 x20008000 −e 0 x20008000 −n "
Linux K e r n e l Image " −d l i n u x . b i n . gz AN. img
22 echo "−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−"
echo −n " ∗ C r e a t i o n o f Image AN. img . . . "
24 f i
echo "Done"
26 echo −n " ∗ S t a r t i n g t f t p −s e r v e r f o r up lo ad ( password may be r e q u i r e d ) . . . "
sudo a t f t p d −−no−f o r k −−v e r b o s e −−daemon . / &
28 echo "Done"
echo " P r e s s any key when t h e u pl oa d i s f i n i s h e d "
30 echo "−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−"
read YN
32 echo −n " ∗ S t o p p i n g t f t p −s e r v e r . . . "
echo "Done"
34 sudo k i l l a l l a t f t p d
Art-Net Node main application:
/∗
2 /∗
/∗
4
/∗
6 /∗
/∗
Art−Net Node Main A p p l i c a t i o n ∗/
W r i t t e n by R a f a e l V e r b i e s t and Maarten Reekmans ∗/
A p r i l −May 2009 ∗/
This program i s f r e e s o f t w a r e : you can r e d i s t r i b u t e i t and/ or modify ∗/
i t under t h e terms o f t h e GNU General P u b l i c L i c e n s e as p u b l i s h e d ∗/
by t h e Free S o f t w a r e Foundation , e i t h e r v e r s i o n 3 o f t h e L i c e n s e , ∗/
86
8 /∗
/∗
10 /∗
/∗
12 /∗
/∗
14 /∗
/∗
16 /∗
Code Listings
or ( a t your o p t i o n ) any l a t e r v e r s i o n . ∗/
∗/
This program i s d i s t r i b u t e d i n t h e hope t h a t i t w i l l be u s e f u l , b u t ∗/
WITHOUT ANY WARRANTY; w i t h o u t even t h e i m p l i e d w a r r a n t y o f ∗/
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See t h e GNU ∗/
General P u b l i c L i c e n s e f o r more d e t a i l s . ∗/
∗/
You s h o u l d have r e c e i v e d a copy o f t h e GNU General P u b l i c L i c e n s e ∗/
a l o n g w i t h t h i s program .
I f not , s e e <h t t p : / /www. gnu . o r g / l i c e n s e s />. ∗/
18 #include
#include
20 #include
#include
22 #include
#include
24 #include
#include
26 #include
#include
28 #include
<s t d i o . h>
< s t d l i b . h>
< s t r i n g . h>
<u n i s t d . h>
<p t h r e a d . h>
< p o l l . h>
< f c n t l . h>
< s i g n a l . h>
<e r r n o . h>
<s y s / s t a t . h>
<s y s / t y p e s . h>
30 #include
#include
32
#include
34 #include
" a r t n e t / a r t n e t . h"
" a r t n e t / p a c k e t s . h"
" ArtNetNode . h"
" ArtNetNodeConfig . c "
36 #define CONFIGFILE " node . b i n c o n f "
38 i n t v e r b o s e =0 , debug =0;
pthread_mutex_t conf_mutex = PTHREAD_MUTEX_INITIALIZER;
40
i n t main ( i n t ar g c , char ∗ a r g v [ ] ) {
42
struct atnode_t mnode ;
/∗ Node runtime d a t a ∗/
struct atnode_t ∗ node = &mnode ;
44
pthread_t t h r e a d _ i d ;
p a r s e o p t s ( a r g c , argv , &debug , &v e r b o s e ) ;
/∗ Get t h e CLI a r g s ∗/
46
/∗ I n i t i a l i s e Art−Net L i b ∗/
node−>l a n = artnet_new ( 0 , v e r b o s e ) ;
48
/∗ C r e a t e a c o n t r o l l e r t h r e a d t h a t p a s s e s c o n f i g d a t a t o t h e worker ∗/
i f ( p t h r e a d _ c r e a t e (& thread_id , NULL, &n o d e _ c o n t r o l l e r , ( void ∗ ) node ) )
50
{
p r i n t f ( " p t h r e a d f a i l e d \n" ) ;
52
}
/∗ Run t h e main a p p l i c a t i o n ( worker ) ∗/
54
node_worker ( node ) ;
/∗ So run t h e main a p p l i c a t i o n ∗/
return 0 ;
56 }
58 /∗
∗ Parse The CLI o p t i o n s
60 ∗/
62 void p a r s e o p t s ( i n t a r g c , char ∗ a r g v [ ] , i n t ∗ debug , i n t ∗ v e r b o s e ) {
int c ;
64
while ( ( c = g e t o p t ( a r g c , argv , "dv" ) ) != −1)
66
switch ( c )
{
68
case ’ d ’ :
∗ debug =1;
70
break ;
case ’ v ’ :
72
∗ v e r b o s e =1;
case ’ ? ’ :
74
i f ( i s p r i n t ( optopt ) )
f p r i n t f ( s t d e r r , "Unknown o p t i o n ‘−%c ’ . \ n" , o p t o p t ) ;
76
exit (1) ;
default :
87
78
abort () ;
}
80 }
82 /∗
∗ Open DMX d e v i c e s
84 ∗/
86 void dmxdev_open ( struct atnode_t ∗ node ) {
int i , fd ;
88
f o r ( i =0; i < 4 ; i ++) {
f d = open ( node−>c o n f i g . i o p o r t [ i ] . d e v i c e n o d e ,
90
O_RDWR | O_NOCTTY | O_NONBLOCK ) ;
i f ( fd < 0)
92
f p r i n t f ( s t d e r r , " D e v i c e %i : OPEN FAILED ! \ n" , i ) ;
94
f p r i n t f ( s t d e r r , " p o r t %i : %s , f d=%i \n" , i , node−>c o n f i g . i o p o r t [ i ] . d e v i c e n o d e , f d ) ;
node−>i o p o r t [ i ] . f d = f d ;
96
}
}
98
100
/∗
∗ Read DMX i n p u t and p u t t h e d a t a i n b u f f e r
∗/
102
104
106
108
110
112
114
116
118
120
122
124
126
128
130
132
134
136
138
140
142
144
146
void dmx_route ( struct atnode_t ∗ node ) {
/∗ This f u n c t i o n r o u t e s DMX i n p u t s ∗/
i n t i , j , l e n , fd , p o r t ;
i n t found =0;
p r i n t f ( "Dmx_route ( node ) \n" ) ;
/∗ Run o v e r t h e I /O t a b l e ∗/
f o r ( i =0; i < node−>c o n f i g . num_ports ; i ++) {
f d = node−>r s e t [ i ] . f d ;
/∗ Find DMX i n p u t s t h a t have d a t a w a i t i n g ∗/
i f ( ( f d > 0 ) && ( node−>r s e t [ i ] . r e v e n t s & POLLIN) ) {
/∗ Read t h i s d a t a ∗/
f p r i n t f ( s t d e r r , " Reading ( ) I /O t a b l e e l e m e n t nr %i ( f d %i ) , " , i , f d ) ;
l e n = r e a d ( fd ,& node−>i o p o r t [ i ] . buf , 5 1 2 ) ;
node−>i o p o r t [ i ] . l e n = l e n ;
i f ( l e n == 0 )
b z e r o (&node−>i o p o r t [ i ] . buf , 5 1 2 ) ;
i f ( l e n >=0) {
f p r i n t f ( s t d e r r , " g o t %i b y t e s , a t t e m p t i n g t o r o u t e t o \n" , l e n ) ;
/∗ Look a t t h e o u t p u t a r r a y o f t h i s i n p u t p o r t ∗/
f o r ( j =0; node−>c o n f i g . r o u t i n g [ i ] . o u t s [ j ] . t y p e !=X \
&& j <node−>c o n f i g . num_ports \
&& found ==0; j ++)
{
i f ( node−>c o n f i g . r o u t i n g [ i ] . o u t s [ j ] . t y p e==DMX) {
/∗ We need t o r e p e a t t h i s d a t a t o a n o t h e r DMX p o r t ∗/
f p r i n t f ( s t d e r r , " \ t \ t \tdmx p o r t %i , " , node−>c o n f i g . r o u t i n g [ i ] . o u t s [ j ] . number ) ;
dmx_write ( node , node−>c o n f i g . r o u t i n g [ i ] . o u t s [ j ] . number ,
node−>i o p o r t [ i ] . buf , l e n ) ;
}
else {
/∗ We need t o o u t p u t t h i s d a t a t o an Art−Net u n i v e r s e ∗/
f p r i n t f ( s t d e r r , " \ t \ t \ tArt−Net \n" ) ;
artnet_send_dmx ( node−>lan , i , ( i n t 1 6 _ t ) l e n , ( u i n t 8 _ t ∗ ) node−>i o p o r t [ i ] . b u f ) ;
/∗ But i t i s n o t r e l e v a n t t o send DMX d a t a t o m u l t i p l e u n i v e r s e s ∗/
found = 1 ;
}
}
found = 0 ;
} e l s e i f ( l e n == −EAGAIN) {
f p r i n t f ( s t d e r r , " no data a q u i r e d a t t h i s time \n" ) ;
} else {
p e r r o r ( " read e r r o r " ) ;
}
node−>r s e t [ i ] . r e v e n t s = 0 ;
}
88
148
Code Listings
}
}
150
void node_worker ( struct atnode_t ∗ node )
152 {
i n t s i g c a t c h =0;
154
/∗ Parent : ArtNetNode C o n t r o l ∗/
156
b z e r o ( node , s i z e o f ( struct atnode_t ) ) ;
158
160
162
164
166
168
170
172
174
176
178
180
182
184
186
188
190
192
194
/∗ C l e a r runtime node ∗/
/∗ Put t h e c o n f i g f i l e i n t h e runtime d a t a ∗/
u p d a t e _ n o d e c o n f i g ( node , n e w c o n f i g ) ;
/∗ Open DMX d e v i c e s ∗/
dmxdev_open ( node ) ;
/∗ S e t Art−Net DMX " d a t a r e c e i v e d " c a t c h e r f u n c t i o n ∗/
artnet_set_dmx_handler ( node−>lan , dmx_handler , ( void ∗ ) node ) ;
/∗ S t a r t Art−Net ∗/
a r t n e t _ s t a r t ( node−>l a n ) ;
// h a n d l e r s ?
while ( 1 )
{
r e b u i l d _ c o n f i g ( node ) ;
p r i n t f ( " Working . . . \ n" ) ;
while ( ! s i g c a t c h ) {
switch ( p o l l ( node−>r s e t , node−>c o n f i g . num_ports , 0 ) ) {
case 0 :
/∗ t i m e o u t ∗/
break ;
case −1:
/∗ e r r o r ∗/
p e r r o r (NULL) ;
i f ( e r r n o != EINTR)
exit (0) ;
break ;
default :
/∗ f d ( ’ s ) r e a d y ∗/
dmx_route ( node ) ;
/∗ Read DMX d a t a ∗/
break ;
}
/∗ I f one o f t h e i n p u t s i s a r t n e t , c a l l l i b a r t n e t t o r e a d ∗/
i f ( node−>ar t n e t _ i n p u t _ u s e d )
{
/∗ a r t n e t _ r e a d can o n l y b l o c k f o r s e co nd s , n o t m i l l i s e c o n d s ∗/
a r t n e t _ r e a d ( node−>lan , 0 ) ;
/∗ t h i s d o e s a l m o s t t h e same t h i n g as b l o c k i n g f o r 5 ms ∗/
usleep (5000) ;
a r t n e t _ r e a d ( node−>lan , 0 ) ;
}
}
}
196 }
198 void ∗ n o d e _ c o n t r o l l e r ( void ∗ node )
{
200
struct n o d e c o n f i g _ t d i s k c o n f i g ;
/∗ C o n f i g u r a t i o n f i l e ∗/
struct n o d e c o n f i g _ t ∗ n e w c o n f i g ;
202
char c o n f f i l e [ 3 0 ] = CONFIGFILE ;
/∗ Path o f c o n f i g f i l e ∗/
struct s t a t s t r e s u l t ;
204
atnode_t ∗ node = ( atnode_t ∗ ) node ;
/∗ Cast t h e parameter o f t h e t h r e a d ∗/
/∗ Get t h e i n i t i a l c o n f i g u r a t i o n f i l e ∗/
206
i f ( s t a t ( c o n f f i l e ,& s t r e s u l t ) >=0) {
i f ( l o a d _ b i n c o n f i g (& d i s k c o n f i g , c o n f f i l e ) >=0) {
208
f p r i n t f ( s t d e r r , " Using d i s k c o n f f i l e . . . \ n" ) ;
n e w c o n f i g = &d i s k c o n f i g ;
210
} else {
f p r i n t f ( s t d e r r , " Using d e f a u l t c o n f . . . \ n" ) ;
212
n e w c o n f i g = &d e f c o n f i g ;
}
214
} else {
f p r i n t f ( s t d e r r , " c o n f f i l e d o e s not e x i s t , u s i n g d e f a u l t c o n f . . . \ n" ) ;
216
n e w c o n f i g = &d e f c o n f i g ;
}
89
218
pthread_mutex_lock(&conf_mutex ) ;
220
pthread_mutex_unlock(&conf_mutex ) ;
}
222
void r e b u i l d _ c o n f i g ( struct atnode_t ∗ node )
224 {
i n t i , j , portnumber ;
226
i n t found =0;
char b u f [ 5 1 2 ] ;
228
/∗ P a r s i n g o f I /O t a b l e ∗/
p r i n t f ( " Updating c o n f i g u r a t i o n . . . \ n" ) ;
230
/∗ S e t Art−Net C o n f i g ∗/
artnet_set_node_type ( node−>lan , ARTNET_NODE) ;
232
/∗ This s h o u l d be s e t b a c k t o z e r o e v e r y time t h e c o n f i g i s u p d a t e d ∗/
node−>a r t n e t _ i n p u t _ u s e d =0;
234
/∗ Run o v e r I /O t a b l e and c o n f i g u r e r s e t and Art−Net p o r t t y p e s ∗/
f o r ( i =0; i <node−>c o n f i g . num_ports ; i ++) {
236
/∗ When unused , s e t t h e r s e t s t o z e r o ∗/
node−>r s e t [ i ] . f d =0;
238
node−>r s e t [ i ] . e v e n t s =0;
/∗ TODO: When unused , s e t t h e Art−Net i n p u t s t o z e r o
240
/∗ a r t n e t _ s e t _ p o r t _ t y p e ( node−>lan , i , ARTNET_DISABLE_OUTPUT, ARTNET_NOTHING) ; ∗ /
/∗ a r t n e t _ s e t _ p o r t _ a d d r ( node−>lan , i , ARTNET_NO_PORT, ARTNET_NOTHING) ; ∗/
242
switch ( node−>c o n f i g . r o u t i n g [ i ] . t y p e ) {
case X: /∗ I n p u t Not Used ∗/
244
p r i n t f ( " I n p u t Not Used \n" ) ;
break ;
246
case DMX: /∗ DMX I n p u t ∗/
p r i n t f ( "DMX I n p u t \n" ) ;
248
portnumber = node−>c o n f i g . r o u t i n g [ i ] . number ; /∗ I /O t a b l e e l e m e n t number t o
p h y s i c a l p o r t number ∗/
node−>r s e t [ i ] . f d=node−>i o p o r t [ portnumber ] . f d ; /∗ F i l l i n t h e p h y s i c a l p o r t number
t o t h e p o l l s e t ∗/
250
node−>r s e t [ i ] . e v e n t s = POLLIN ; /∗ We w i l l be w a i t i n g f o r i n p u t d a t a on t h i s c h a r
d e v i c e ∗/
r e a d ( node−>r s e t [ i ] . fd , buf , 5 1 2 ) ; /∗ S e t t h e p o r t t o r e a d mode ∗/
252
/∗ Run o v e r o u t p u t s and f i n d t h e f i r s t Art−Net o u t p u t . ∗/
/∗ S e t i t t o ARTNET_INPUT_PORT (INPUT on t h e Art−Net n e t w o r k ) ∗/
254
/∗ The row number o f t h e I /O t a b l e i s used as t h e Art−Net p o r t number ∗/
f o r ( j =0; j < node−>c o n f i g . num_ports && found ==0; j ++)
256
{
i f ( node−>c o n f i g . r o u t i n g [ i ] . o u t s [ j ] . t y p e == ARTNET)
258
{
a r t n e t _ s e t _ p o r t _ t y p e ( node−>lan , i , ARTNET_ENABLE_INPUT, ARTNET_PORT_DMX) ;
260
/∗ The u n i v e r s e number i s e x t r a c t e d from t h e o u t p u t p a r t o f t h e I /O t a b l e ∗/
artnet_set_port_addr ( node−>lan , i , ARTNET_INPUT_PORT, node−>c o n f i g . r o u t i n g [ i ] .
o u t s [ j ] . number ) ;
262
/∗ There can be o n l y one Art−Net o u t p u t p e r DMX i n p u t , ∗/
/∗ so we s t o p a f t e r h a v i n g found one ∗/
264
found = 1 ;
}
266
}
found = 0 ;
268
break ;
case ARTNET: /∗ Art−Net I n p u t ∗/
270
p r i n t f ( " Art−Net I n p u t \n" ) ;
node−>a r t n e t _ i n p u t _ u s e d =1;
272
/∗ The row number o f t h e I /O t a b l e i s used as t h e Art−Net p o r t number ∗/
a r t n e t _ s e t _ p o r t _ t y p e ( node−>lan , i , ARTNET_ENABLE_OUTPUT, ARTNET_PORT_DMX) ;
274
/∗ The u n i v e r s e number i s e x t r a c t e d from t h e o u t p u t p a r t o f t h e I /O t a b l e ∗/
artnet_set_port_addr ( node−>lan , i , ARTNET_OUTPUT_PORT, node−>c o n f i g . r o u t i n g [ i ] .
number ) ;
276
break ;
}
278
}
}
280
i n t dmx_handler ( artnet_node n , i n t p o r t , void ∗d )
282 {
90
Code Listings
/∗ This f u n c t i o n r o u t e s Art−Net i n p u t s ∗/
/∗ I t i s c a l l e d by l i b a r t n e t when an ArtDMX p a c k e t i s r e c e i v e d ∗/
u i n t 8 _ t ∗ data ;
struct atnode_t ∗ node = ( struct atnode_t ∗ ) d ;
int len , i ;
p r i n t f ( "De p o o r t i s %d\n" , p o r t ) ;
data=artnet_read_dmx ( n , p o r t , &l e n ) ;
p r i n t f ( "De ontvangen data v o l g t : \ n" ) ;
f o r ( i =1; i <( l e n / 8 ) ; i ++)
{
p r i n t f ( "C %3d : %3d\ t " , i , data [ i −1]) ;
i f ( i %8==0) p r i n t f ( " \n" ) ;
}
/∗ Find a l l r e l e v a n t o u t p u t s ∗/
/∗ The I /O t a b l e row number was used as t h e Art−Net ’ p o r t ’ i n ∗/
/∗ r e b u i l d _ c o n f i g , so we can u s e i t t o f i n d t h e c o r r e c t t a b l e e n t r y now ∗/
f o r ( i =0; i <node−>c o n f i g . num_ports ; i++ )
{
i f ( node−>c o n f i g . r o u t i n g [ p o r t ] . o u t s [ i ] . t y p e == DMX)
{
dmx_write ( node , node−>c o n f i g . r o u t i n g [ p o r t ] . o u t s [ i ] . number , data , l e n ) ;
}
else
{
/∗ I t seems s t u p i d t o r e p e a t Art−Net p a c k e t s o f a c e r t a i n u n i v e r s e ∗/
/∗ t o a n o t h e r u n i v e r s e . I f t h i s would e v e r be r e q u i r e d f o r some reason , ∗/
/∗ i t would have t o be done h e r e ∗/
}
}
return 0 ;
284
286
288
290
292
294
296
298
300
302
304
306
308
310
312
}
314
i n t dmx_write ( struct atnode_t ∗ node , i n t p o r t n r , char data [ ] , i n t l e n )
316 {
struct p o l l f d wset [ 1 ] ;
318
wset [ 0 ] . f d=node−>i o p o r t [ p o r t n r ] . f d ;
wset [ 0 ] . e v e n t s = POLLOUT;
320
wset [ 0 ] . r e v e n t s = 0 ;
i f ( p o l l ( wset , 1 , 1 ) > 0 ) {
/∗ Wait a maximum o f 1ms f o r t h e p o r t t o become
r e a d y ∗/
322
i f ( wset [ 0 ] . r e v e n t s & POLLOUT) { /∗ I f i t i s r e a d y w i t h i n t h i s time , send t h e frame
∗/
/∗ Write t h e d a t a ∗/
324
/∗ Every once i n a w h i l e , w h i l e w r i t i n g , we g e t a Resource t e m p o r a r i l y u n a v a i l a b l e
∗/
/∗ e r r o r . The c a u s e o f t h i s has n o t been t r a c k e d down y e t , b u t t h e w h i l e l o o p b e l o w
e n s u r e s ∗/
326
/∗ t h a t t h e d a t a g e t s w r i t t e n i n any c a s e ∗/
while ( w r i t e ( wset [ 0 ] . fd , data , l e n ) < 0 ) ;
328
f p r i n t f ( s t d e r r , " wrote t o f d %i \n" , wset [ 0 ] . f d ) ;
}
330
else {
f p r i n t f ( s t d e r r , " f d %i not r e a d y ! \ n" , wset [ 0 ] . f d ) ;
332
}
}
334
else {
/∗ Port d i d n o t become r e a d y i n time , d i s c a r d i n g t h e frame ∗/
336
/∗ This s h o u l d n o t happen f r e q u e n t l y ! ∗/
p r i n t f ( " D i s c a r d i n g data frame ! \ n" ) ;
338
}
f p r i n t f ( s t d e r r , " \n" ) ;
340 }
Art-Net Node header file:
/∗ D e f i n e s ∗/
2
#define X ( i n t ) 0
4 #define DMX 1
#define ARTNET 2
91
6
/∗ i p o r t c o n f i g u r a t i o n d a t a ∗/
8 struct ioport_c_t {
char d e v i c e n o d e [ 3 0 ] ;
10
i n t port_number ;
};
12
/∗ i p o r t runtime d a t a ∗/
14 struct i o p o r t _ r _ t {
int fd ;
16
int v a l i d ;
char b u f [ 5 1 2 ] ;
18
int l e n ;
};
20
/∗ o u t p u t f o r r o u t i n g ∗/
22 struct ou tp ut {
int type ;
24
i n t number ;
};
26
/∗ Routing m a t r i x in−o u t ∗/
28 struct i o m a t r i x {
int type ;
30
i n t number ;
struct o ut pu t o u t s [ 4 ] ;
32 } ;
34 /∗ C o n f i g u r a t i o n f i l e ∗/
36 struct n o d e c o n f i g _ t {
struct ioport_c_t i o p o r t [ 4 ] ;
38
struct i o m a t r i x r o u t i n g [ 4 ] ;
i n t num_ports ;
40
char a r t s u b ;
char ipmode ;
42
char i p a d d r [ 1 7 ] ;
char i p s u b [ 1 7 ] ;
44
char short_name [ 1 6 ] ;
char long_name [ 6 4 ] ;
46 } ;
48 /∗ Node runtime d a t a ∗/
struct atnode_t {
50
char ∗ c o n f i g _ f i l e ;
char caddr [ 3 ] ;
52
struct n o d e c o n f i g _ t c o n f i g ;
struct i o p o r t _ r _ t i o p o r t [ 4 ] ;
54
struct p o l l f d r s e t [ 4 ] ;
int r s e t n ;
56
int i n i t i a l i z e d ;
artnet_node l a n ;
58
i n t a r t n e t _ i n p u t_ u s e d ;
};
60
62 #i f n d e f HAVE_DEFCONFIG
#define HAVE_DEFCONFIG
64 struct n o d e c o n f i g _ t d e f c o n f i g = {
. i p s u b = {0 x f f , 0 x f f , 0 x f f , 0 } ,
66
. artsub = 0 ,
. num_ports = 4 ,
68
. ipmode = 0 ,
// . i p a d d r = " 1 7 2 . 1 6 . 2 4 1 . 1 2 3 " ,
70
. ipaddr = " 192.168.1.123 " ,
. ipsub = " 255.255.255.0 " ,
72
. short_name = "KBDesign atNode " ,
. long_name = "KB D e s i g n Art−Net Node" ,
74
. i o p o r t = { { . d e v i c e n o d e = " / dev /dmx0" , . port_number=0 } ,
{ . d e v i c e n o d e = " / dev /dmx1" , . port_number=1 } ,
92
76
78
80
82
84
86
88
90
92
Code Listings
{ . d e v i c e n o d e = " / dev /dmx2" , . port_number=2 } ,
{ . d e v i c e n o d e = " / dev /dmx3" , . port_number=3 } , } ,
. r o u t i n g = { { . t y p e = DMX, . number=0 , . o u t s = { { . t y p e = ARTNET, . number=1 } ,
{ . t y p e = X, . number=0 } ,
‘
{ . t y p e = X, . number=0 } ,
{ . t y p e = X, . number=0 } , } , } ,
{ . t y p e = ARTNET, . number=8 , . o u t s = { { . t y p e = DMX, . number=2 } ,
{ . t y p e = X, . number=0 } ,
{ . t y p e = X, . number=0 } ,
{ . t y p e = X, . number=0 } , } , } ,
{ . t y p e = X, . number=2 , . o u t s = { { . t y p e = X, . number=0 } ,
{ . t y p e = X, . number=0 } ,
{ . t y p e = X, . number=0 } ,
{ . t y p e = X, . number=0 } , } , } ,
{ . t y p e = X, . number=3 , . o u t s = { { . t y p e = X, . number=0 } ,
{ . t y p e = X, . number=0 } ,
{ . t y p e = X, . number=0 } ,
{ . t y p e = X, . number=0 } , } , } , } ,
94
};
96 #endif
98 /∗ P r o t o t y p e s ∗/
100 void node_worker ( void ) ;
void n o d e _ c o n t r o l l e r ( void ) ;
102 void r e b u i l d _ c o n f i g ( struct atnode_t ∗ node ) ;
void p a r s e o p t s ( i n t a r g c , char ∗ a r g v [ ] , i n t ∗ debug , i n t ∗ v e r b o s e ) ;
104 void dmxdev_open ( struct atnode_t ∗ node ) ;
void atdmx_write ( struct atnode_t ∗ node ) ;
106 void atdmx_read ( struct atnode_t ∗ node ) ;
i n t u p d a t e _ n o d e c o n f i g ( struct atnode_t ∗ node , struct n o d e c o n f i g _ t ∗ newconf ) ;
108 i n t l o a d _ b i n c o n f i g ( struct n o d e c o n f i g _ t ∗ c o n f i g , char ∗ c o n f i g _ f i l e ) ;
i n t dmx_handler ( artnet_node n , i n t p o r t , void ∗d ) ;
110 i n t dmx_write ( struct atnode_t ∗ node , i n t p o r t n r , char data [ ] , i n t l e n ) ;
Art-Net Node configuration code:
1
3
5
7
9
11
i n t atnode_change_short_name ( struct atnode_t ∗ node , const char ∗ s t r i n g ) {
i f ( node−>l a n
&& ( strncmp ( ( const char ∗ ) &node−>c o n f i g . short_name , ( const char ∗ ) s t r i n g , ( s i z e _ t )
16)
||
( ! node−> i n i t i a l i z e d ) )
) {
s t r n c p y ( ( char ∗ )&node−>c o n f i g . short_name , s t r i n g , ( s i z e _ t ) 1 6 ) ;
artnet_set_short_name ( node−>lan , s t r i n g ) ;
return 1 ;
}
return 0 ;
}
13
15
17
19
21
23
25
27
29
i n t atnode_change_long_name ( struct atnode_t ∗ node , const char ∗ s t r i n g ) {
i f ( node−>l a n
&& (
( ! node−> i n i t i a l i z e d )
||
strncmp ( ( const char ∗ ) &node−>c o n f i g . long_name , ( const char ∗ ) s t r i n g , ( s i z e _ t )
64) )
) {
s t r n c p y ( ( char ∗ )&node−>c o n f i g . long_name , s t r i n g , ( s i z e _ t ) 6 4 ) ;
artnet_set_long_name ( node−>lan , s t r i n g ) ;
return 1 ;
}
return 0 ;
}
i n t atnode_change_artsub ( struct atnode_t ∗ node , const char a r t s u b ) {
i f ( ( node−>l a n )
&& ( ( node−>c o n f i g . a r t s u b != a r t s u b ) | | ( ! node−> i n i t i a l i z e d ) ) ) {
node−>c o n f i g . a r t s u b = a r t s u b ;
artnet_set_subnet_addr ( node−>lan , a r t s u b ) ;
f p r i n t f ( s t d e r r , " Updated Art−Net s u b n e t : %i \n" , a r t s u b ) ;
93
31
33
return 1 ;
}
return 0 ;
}
35 i n t atnode_change_ipaddr ( struct atnode_t ∗ node , const char ∗ i p a d d r ) {
i f ( strncmp ( ( const char ∗ )&node−>c o n f i g . i p a d d r , ( const char ∗ ) i p a d d r , ( s i z e _ t ) 1 7 ) ) {
37
s t r n c p y ( ( char ∗ )&node−>c o n f i g . i p a d d r , i p a d d r , ( s i z e _ t ) 1 7 ) ;
f p r i n t f ( s t d e r r , " Updated IP : %s \n" , i p a d d r ) ;
39
return 1 ;
}
41
return 0 ;
}
43
i n t atnode_change_ipsub ( struct atnode_t ∗ node , const char ∗ i p s u b ) {
45
i f ( strncmp ( ( const char ∗ )&node−>c o n f i g . i p s u b , ( const char ∗ ) i p s u b , ( s i z e _ t ) 1 7 ) ) {
s t r n c p y ( ( char ∗ )&node−>c o n f i g . i p s u b , i p s u b , ( s i z e _ t ) 1 7 ) ;
47
f p r i n t f ( s t d e r r , " Updated s u b n e t : %s \n" , i p s u b ) ;
return 1 ;
49
}
return 0 ;
51 }
53 i n t atnode_change_devicenode ( struct atnode_t ∗ node , i n t p o r t , const char ∗ s t r i n g ) {
i f ( strncmp ( ( const char ∗ )&node−>c o n f i g . i o p o r t [ p o r t ] . d e v i c e n o d e , ( const char ∗ ) s t r i n g
, ( size_t ) 30) ) {
55
s t r n c p y ( ( char ∗ )&node−>c o n f i g . i o p o r t [ p o r t ] . d e v i c e n o d e , s t r i n g , ( s i z e _ t ) 1 7 ) ;
f p r i n t f ( s t d e r r , " Updated p o r t %i d e v i c e n o d e t o : \"% s \"\ n" , p o r t , s t r i n g ) ;
57
return 1 ;
} else {
59
f p r i n t f ( s t d e r r , " Port %i d e v i c e n o d e not changed \n" , p o r t ) ;
}
61
return 0 ;
}
63
/∗
65 ∗ Put t h e I /O m a t r i x i n t o t h e runtime c o n f i g
∗/
67
i n t a t no d e _l o ad _ i om a tr i x ( struct atnode_t ∗ node , struct n o d e c o n f i g _ t ∗ newconf ) {
69
i n t r e t v a l =0;
memcpy ( node−>c o n f i g . r o u t i n g , newconf−>r o u t i n g , 4∗ s i z e o f ( struct i o m a t r i x ) ) ;
71
p r i n t f ( "De e e r s t e i n p u t i s %d\n" , node−>c o n f i g . r o u t i n g [ 0 ] . number ) ;
return r e t v a l ++;
73 }
75
/∗ l o a d _ b i n c o n f i g − l o a d c o n f i g s t r u c t dump from f i l e ∗/
77
i n t l o a d _ b i n c o n f i g ( struct n o d e c o n f i g _ t ∗ c o n f i g , char ∗ c o n f i g _ f i l e ) {
int fd ;
f d = open ( c o n f i g _ f i l e , O_RDONLY) ;
81
i f ( ! fd )
return f d ;
83
r e a d ( fd , c o n f i g , s i z e o f ( struct n o d e c o n f i g _ t ) ) ;
c l o s e ( fd ) ;
85
return 0 ;
}
87
/∗ u p d a t e _ n o d e c o n f i g − u p d a t e e n t i r e c o n f i g u r a t i o n a t once ∗/
89 i n t u p d a t e _ n o d e c o n f i g ( struct atnode_t ∗ node , struct n o d e c o n f i g _ t ∗ newconf ) {
char p o r t a d d r [ 4 ] ;
91
int i , r e t v a l = 0 ;
node−> i n i t i a l i z e d = 0 ;
93
r e t v a l += atnode_change_short_name ( node , ( const char ∗ )&newconf−>short_name ) ;
r e t v a l += atnode_change_long_name ( node , ( const char ∗ )&newconf−>long_name ) ;
95
r e t v a l += atnode_change_artsub ( node , ( const char ) newconf−>a r t s u b ) ;
79
97
99
r e t v a l += atnode_change_ipaddr ( node , ( const char ∗ )&newconf−>i p a d d r ) ;
r e t v a l += atnode_change_ipsub ( node , ( const char ∗ )&newconf−>i p s u b ) ;
94
Code Listings
i f ( node−>c o n f i g . num_ports != newconf−>num_ports )
node−>c o n f i g . num_ports = newconf−>num_ports ;
101
107
f o r ( i =0; i < newconf−>num_ports ; i ++) {
p o r t a d d r [ i ] = newconf−>i o p o r t [ i ] . port_number ;
atnode_change_devicenode ( node , i , ( const char ∗ ) &newconf−>i o p o r t [ i ] . d e v i c e n o d e ) ;
}
r e t v a l += at n od e _ lo a d _i o m at r ix ( node , newconf ) ;
109
node−> i n i t i a l i z e d = 1 ;
103
105
return r e t v a l ;
111
}
AT91RM9200 DMX driver:
1 /∗
∗ D r i v e r f o r Atmel AT91RM9200 S e r i a l p o r t DMX512
3 ∗
∗ C o p y r i g h t (C) 2007 Thomas Langewouters
5 ∗
∗ Note : When t h i s d r i v e r i s used i n a commercial p r o d u c t
7 ∗ and used i n t h e Linux k e r n e l , t h e k e r n e l l i c e n s e r e q u i r e s
∗
t h i s d r i v e r be made GPL, and s o u r c e s made a v a i l a b l e .
9 ∗/
#include <l i n u x / c o n f i g . h>
/∗ backward c o m p a t i b i l i t y ∗/
11 #include <l i n u x / module . h>
/∗ dynamic l o a d i n g o f modules ∗/
#include <l i n u x / i o p o r t . h>
/∗ request_mem_region ∗/
13 #include <l i n u x / s l a b . h>
/∗ k m a l l o c ( ) ∗/
#include <l i n u x / i n i t . h>
/∗ m o d u l e _ i n i t ( ) ) ∗/
15 #include <l i n u x / p r o c _ f s . h>
/∗ / p r o c e n t r y ∗/
#include <l i n u x / c l k . h>
/∗ PCM c l k s o u r c e c f g ∗/
17 #include <l i n u x / s p i n l o c k . h>
/∗ s p i n l o c k s ∗/
#include <l i n u x / p l a t f o r m _ d e v i c e . h>
/∗ p l a t f o r m d e v i c e ∗/
19 #include <l i n u x /dma−mapping . h>
/∗ dma f u n c t i o n s ∗/
#include <l i n u x / i n t e r r u p t . h>
/∗ i n t e r r u p t s ∗/
21 #include <l i n u x / p o l l . h>
/∗ p o l l i n g f u n c t i o n s ∗/
#include <asm/ u a c c e s s . h>
/∗ copy_to_user ( ) ∗/
23 #include <asm/ a r c h / at91_gpio . h>
/∗ maintech g p i o a b s t r a c t i o n ∗/
#include <asm/ i o . h>
/∗ w r i t e l ( ) , r e a d l ( ) ∗/
25 #include <asm/ a r c h / at91rm9200_usart . h>
/∗ r e g i s t e r d e f i n i t i o n s ∗/
#include <asm/ a r c h / at91rm9200_pdc . h>
/∗ r e g i s t e r d e f i n i t i o n s ∗/
27 #include <asm/ a r c h / board . h>
/∗ s t r u c t at91_uart_data ∗/
#include "dmx . h"
29 /∗++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++∗/
/∗ g l o b a l d a t a ∗/
31 s t a t i c struct dmxdevice dmxdevices [ 4 ] =
{
33
{ . s t a t e = ( struct w s t a t e ∗ ) 0 } ,
{ . s t a t e = ( struct w s t a t e ∗ ) 0 } ,
35
{ . s t a t e = ( struct w s t a t e ∗ ) 0 } ,
{ . s t a t e = ( struct w s t a t e ∗ ) 0}
37 } ;
struct p r o c _ d i r _ e n t r y ∗ proc_ entry ;
39 s t a t i c struct p l a t f o r m _ d r i v e r at91_dmx_driver = {
. probe
= dmx_probe ,
41
. remove
= __devexit_p ( dmx_remove ) ,
. driver
= {
43
. name = " at91_dmxdevice " ,
. owner = THIS_MODULE,
45
},
};
47 s t a t i c struct f i l e _ o p e r a t i o n s dmxdev_fops = {
. owner
= THIS_MODULE,
49
. open
= dmx_cdev_open ,
. r e l e a s e = dmx_cdev_release ,
51
. write
= dmx_cdev_write ,
. read
= dmx_cdev_read ,
53
. ioctl
= dmx_cdev_ioctl ,
. poll
= dmx_cdev_poll
95
55 } ;
/∗++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++∗/
57 /∗ s t a t e s d e f i n i t i o n s ∗/
s t a t i c struct w s t a t e i d l e s = {
59
. events = 0 ,
. description = " Inactive " ,
61
. setup = idles_setup ,
. service = idles_service
63 } ;
s t a t i c struct w s t a t e wlb = {
65
. e v e n t s = (AT91_US_TIMEOUT | AT91_US_RXBRK) ,
. d e s c r i p t i o n = " Waiting f o r b r e a k c o n d i t i o n on l i n e " ,
67
. s e t u p = wlb_setup ,
. s e r v i c e = wlb_service
69 } ;
s t a t i c struct w s t a t e wsc = {
71
/∗ RXBUFF> n e x t c o u n t e r r e a c h e s z e r o and c o u n t e r r e g r e a c h e s z e r o ∗/
/∗ ENDRX > RCount r e g r e a c h e s z e r o ∗/
73
. e v e n t s = (AT91_US_TIMEOUT | AT91_US_ENDRX) ,
. d e s c r i p t i o n = " Waiting f o r s t a r t code " ,
75
. s e t u p = wsc_setup ,
. s e r v i c e = wsc_service
77 } ;
s t a t i c struct w s t a t e f b u f = {
79
. e v e n t s = (AT91_US_TIMEOUT | AT91_US_RXBRK | AT91_US_FRAME | AT91_US_ENDRX) ,
. d e s c r i p t i o n = " R e c e i v i n g c h a n n e l data " ,
81
. setup = fbuf_setup ,
. service = fbuf_service
83 } ;
s t a t i c struct w s t a t e t l b = {
85
. e v e n t s = AT91_US_ENDTX,
. d e s c r i p t i o n = " Transmit L i n e Break " ,
87
. setup = tlb_setup ,
. service = tlb_service
89 } ;
s t a t i c struct w s t a t e tmab = {
91
. e v e n t s = AT91_US_TXEMPTY,
. d e s c r i p t i o n = " Transmit Mark−A f t e r Break " ,
93
. s e t u p = tmab_setup ,
. s e r v i c e = t m a b _s e r v ic e
95 } ;
s t a t i c struct w s t a t e t b u f = {
97
. e v e n t s = AT91_US_TXEMPTY,
. d e s c r i p t i o n = " S e n d i n g DMX data " ,
99
. s e t u p = tbuf_setup ,
. service = tbuf_service
101 } ;
/∗++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++∗/
103 /∗ t o o l f u n c t i o n s ∗/
/∗ n e x t b u f f e r − r e t u r n p o i n t e r t o n e x t wbuf ∗/
105 s t a t i c void ∗ n e x t b u f f e r ( struct dmxdevice ∗ dev ) {
return ( dev−>wbuf == &dev−>b u f f e r [ 0 ] )
107
? &(dev−>b u f f e r [ 1 ] )
: &(dev−>b u f f e r [ 0 ] ) ;
109 }
111 /∗ i d e n t d e v − u a r t number from p o i n t e r ∗/
s t a t i c i n l i n e i n t i d e n t d e v ( struct dmxdevice ∗ d e v i c e ) {
113
int i ;
f o r ( i =0; i <4; i ++)
115
i f ( d e v i c e == &dmxdevices [ i ] )
return i ;
117
return −1;
}
119 /∗ t o s t a t e − change t h e worker p r o c e s s s t a t e ∗/
s t a t i c void t o s t a t e ( struct dmxdevice ∗ d e v i c e ,
121
struct w s t a t e ∗ n e x t s t a t e ,
unsigned i n t i n t s r c
)
123 {
/∗ k e e p p o i n t e r t o p r e v i o u s s t a t e ∗/
96
125
struct w s t a t e ∗ p r e v s t a t e= d e v i c e −>s t a t e ;
127
i f ( p r e v s t a t e != n e x t s t a t e )
p r i n t k (KERN_DEBUG "DMX%i : s t a t e change (% s )−>(%s ) \n" ,
identdev ( device ) ,
( p r e v s t a t e ? p r e v s t a t e −>d e s c r i p t i o n : "NULL" ) ,
n e x t s t a t e −>d e s c r i p t i o n ) ;
/∗ r e q u e s t e d s t a t e change , l e t s e r v i c e ( ) c l e a n up
∗ t h e custom UART c o n f i g u r a t i o n o f t h e c u r r e n t s t a t e ∗/
i f ( i n t s r c == 0 ) {
UART_PUT_IDR( d e v i c e , −1) ;
p r e v s t a t e −>s e r v i c e ( d e v i c e , n e x t s t a t e , 0 ) ;
}
/∗ u p d a t e s t r u c t dmxdevice ∗/
d e v i c e −>s t a t e = n e x t s t a t e ;
/∗ c a l l s t a t e s e t u p code ∗/
n e x t s t a t e −>s e t u p ( d e v i c e , p r e v s t a t e , i n t s r c ) ;
129
131
133
135
137
139
141
Code Listings
}
143 /∗++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++∗/
/∗ i d l e s − i d l e s t a t e ∗/
145 s t a t i c void i d l e s _ s e t u p ( struct dmxdevice ∗ d e v i c e ,
struct w s t a t e ∗ p r e v s t a t e ,
147
int i n t s r c
)
{
149
/∗ i n i t i a l i z e b u f f e r p o i n t e r s ∗/
d e v i c e −>wbuf = &( d e v i c e −>b u f f e r [ 0 ] ) ;
151
d e v i c e −>i b u f = &( d e v i c e −>b u f f e r [ 1 ] ) ;
/∗ c l e a n s ( l | t ) a t e ∗/
153
d e v i c e −>i b u f −>t o u c h e d = 0 ;
d e v i c e −>wbuf−>t o u c h e d = 0 ;
155
/∗ u p d a t e i n t e r r u p t c o n f i g u r a t i o n ∗/
UART_PUT_IDR( d e v i c e , ~( d e v i c e −>s t a t e −>e v e n t s ) ) ;
157
UART_PUT_IER( d e v i c e ,
d e v i c e −>s t a t e −>e v e n t s ) ;
}
159 s t a t i c void i d l e s _ s e r v i c e ( struct dmxdevice ∗ d e v i c e ,
struct w s t a t e ∗ r e q s t a t e ,
161
int i n t s r c
)
{
163
/∗ c l e a r s t a t i s t i c s ∗/
d e v i c e −>o v e r f = 0 ;
165
d e v i c e −>f e r r = 0 ;
/∗ d e v i c e has n o t r e c e i v e d data , b u t s i l e n c e has n o t y e t
167
∗ been c o n f i r m e d . ∗/
d e v i c e −>s i l e n c e = 0 ;
169
i f ( r e q s t a t e == &wlb )
/∗ &i b u f = 0 −−> ’ no b u f f e r a v a i l a b l e now ’ ∗/
171
d e v i c e −>i b u f = 0 ;
}
173 /∗ w l b − Wait f o r l i n e b r e a k ∗/
s t a t i c void wlb_setup
( struct dmxdevice ∗ d e v i c e ,
175
struct w s t a t e ∗ p r e v s t a t e ,
int i n t s r c
)
177 {
i f ( u n l i k e l y ( ( i n t s r c & AT91_US_TIMEOUT )
179
&&
( ! d e v i c e −>p a t i e n c e ) )
) {
/∗ r e a s o n we e n t e r e d t h i s s t a t e : TIMEOUT ∗/
181
i f ( d e v i c e −>s i l e n c e == 0 ) {
/∗ make i b u f f e r p o i n t e r s a y ’ s i l e n c e ’ ∗/
183
d e v i c e −>i b u f = ( struct dmxbuffer ∗ ) −1;
/∗ wake up u s e r s p a c e ∗/
185
w a k e _ u p _ i n t e r r u p t i b l e (& d e v i c e −>i b u f q ) ;
/∗ mark p o r t as i d l e ∗/
187
d e v i c e −>s i l e n c e = 1 ;
}
189
}
i f ( u n l i k e l y ( i n t s r c & AT91_US_FRAME) )
191
/∗ we ’ ve had a f r a m i n g e r r o r ∗/
d e v i c e −>f e r r ++;
193
/∗ r e s t o r e our p a t i e n c e ∗/
d e v i c e −>p a t i e n c e = DMX_TO_PATIENCE;
97
/∗ r e s e t t i m e o u t ∗/
UART_PUT_CR( d e v i c e , AT91_US_RSTSTA) ;
UART_PUT_CR( d e v i c e , AT91_US_RETTO | AT91_US_STTTO) ;
/∗ u p d a t e i n t e r r u p t c o n f i g u r a t i o n ∗/
UART_PUT_IDR( d e v i c e , ~( d e v i c e −>s t a t e −>e v e n t s ) ) ;
UART_PUT_IER( d e v i c e ,
d e v i c e −>s t a t e −>e v e n t s ) ;
195
197
199
201 }
s t a t i c void w l b _ s e r v i c e ( struct dmxdevice ∗ d e v i c e ,
203
struct w s t a t e ∗ r e q s t a t e ,
int i n t s r c
)
205 {
if (! intsrc ) {
207
/∗ n o t s e r v i c i n g an i n t e r r u p t . a b o r t s t a t e ∗/
return ;
209
}
i f ( i n t s r c & AT91_US_RXBRK) {
211
t o s t a t e ( d e v i c e ,& wsc , i n t s r c ) ;
return ;
213
} e l s e i f ( i n t s r c & AT91_US_TIMEOUT) {
/∗ r e s t a r t t i m e o u t c o u n t e r ∗/
215
UART_PUT_CR( d e v i c e , AT91_US_RETTO | AT91_US_STTTO) ;
d e v i c e −>p a t i e n c e −−;
217
i f ( d e v i c e −>p a t i e n c e != 0 )
return ;
219
t o s t a t e ( d e v i c e ,& wlb , i n t s r c ) ;
}
221 }
/∗ wsc − Wait f o r s t a r t code ∗/
223 s t a t i c void wsc_setup ( struct dmxdevice ∗ d e v i c e ,
struct w s t a t e ∗ p r e v s t a t e ,
225
int i n t s r c
)
{
227
char n u l l ;
/∗ we ’ r e v e r y p a t i e n t a g a i n f o r d a t a ∗/
229
d e v i c e −>p a t i e n c e = DMX_TO_PATIENCE;
/∗ we SHOULD NOT c l e a r s t a t u s f l a g s (RSTSTA) here ,
231
∗ t h a t would c a u s e t h e UART t o go b a z e r k ! ! ! ∗/
/∗ o b t a i n p e r i o d i c t i m e o u t i n t e r r u p t ∗/
UART_PUT_CR( d e v i c e , AT91_US_RETTO | AT91_US_STTTO) ;
/∗ c o n f i g u r e DMA t r a n s f e r ∗/
d e v i c e −>wbuf−>dma_addr = dma_map_single ( d e v i c e −>dev ,
d e v i c e −>wbuf−>data ,
5 1 3 , DMA_FROM_DEVICE) ;
/∗ DMA t r a n s f e r f o r S t a r t C o d e ∗/
UART_PUT_RCR( d e v i c e , 1 ) ;
UART_PUT_RPR( d e v i c e , d e v i c e −>wbuf−>dma_addr ) ;
/∗ DMA t r a n f e r f o r c h a n n e l d a t a i s Next ∗/
UART_PUT_RNCR( d e v i c e , 5 1 2 ) ;
UART_PUT_RNPR( d e v i c e , d e v i c e −>wbuf−>dma_addr + s i z e o f ( char ) ) ;
/∗ C l e a r r e c e i v e r r e g i s t e r , j u s t t o be s u r e . . . ∗/
n u l l = UART_GET_CHAR( d e v i c e ) ;
/∗ e n a b l e PDC t r a n s f e r ∗/
UART_PUT_PTCR( d e v i c e , AT91_PDC_RXTEN) ;
/∗ u p d a t e i n t e r r u p t c o n f i g u r a t i o n ∗/
UART_PUT_IDR( d e v i c e , ~( d e v i c e −>s t a t e −>e v e n t s ) ) ;
UART_PUT_IER( d e v i c e ,
d e v i c e −>s t a t e −>e v e n t s ) ;
233
235
237
239
241
243
245
247
249
251
}
253
255
257
259
261
263
s t a t i c void w s c _ s e r v i c e
( struct dmxdevice ∗ d e v i c e ,
struct w s t a t e ∗ r e q s t a t e ,
int i n t s r c
)
{
struct w s t a t e ∗ n e x t s t a t e = 0 ;
char f i r s t b y t e = 0 ;
i f ( i n t s r c & AT91_US_TIMEOUT) {
/∗ r e s t a r t t i m e o u t c o u n t e r ∗/
UART_PUT_CR( d e v i c e , AT91_US_RETTO | AT91_US_STTTO) ;
d e v i c e −>p a t i e n c e −−;
}
98
265
267
269
271
273
275
277
279
281
283
285
Code Listings
i f ( i n t s r c & AT91_US_ENDRX) {
dma_sync_single_for_cpu (
d e v i c e −>dev , d e v i c e −>wbuf−>dma_addr ,
1 , DMA_FROM_DEVICE
);
f i r s t b y t e = d e v i c e −>wbuf−>data [ 0 ] ;
dma_sync_single_for_device ( d e v i c e −>dev , d e v i c e −>wbuf−>dma_addr ,
1 , DMA_FROM_DEVICE
);
/∗ i s t h i s a s t a r t code ? ∗/
p r i n t k (KERN_DEBUG "DMX%i : r e c e i v e d 0x%02x\n" , i d e n t d e v ( d e v i c e ) , f i r s t b y t e ) ;
i f ( f i r s t b y t e == 0 )
n e x t s t a t e = &f b u f ;
}
/∗ e x c e p t i o n s : l e a v e t h e s t a t e p r e m a t u r e l y ∗/
if (
( ! intsrc
)
||
( i n t s r c & AT91_US_FRAME )
||
( ! d e v i c e −>p a t i e n c e
)
||
( firstbyte
)
){
/∗ d i s a b l e t h e DMA t r a n s f e r ∗/
UART_PUT_PTCR( d e v i c e , AT91_PDC_RXTDIS) ;
dma_unmap_single ( d e v i c e −>dev , d e v i c e −>wbuf−>dma_addr ,
5 1 3 , DMA_FROM_DEVICE) ;
287
( ! d e v i c e −>p a t i e n c e )
p r i n t k (KERN_DEBUG "DMX%i : WSC TIMEOUT\n" , i d e n t d e v ( d e v i c e ) ) ;
if ( firstbyte )
p r i n t k (KERN_DEBUG "DMX%i : SC != z e r o \n" , i d e n t d e v ( d e v i c e ) ) ;
/∗ n o t s e r v i c i n g an i n t e r r u p t . a b o r t s t a t e ∗/
if (! intsrc )
return ;
n e x t s t a t e = &wlb ;
if
289
291
293
295
297
}
if ( nextstate )
t o s t a t e ( device , nextstate , i n t s r c ) ;
299 }
/∗ f b u f − F i l l b u f f e r ∗/
301 s t a t i c void f b u f _ s e t u p ( struct dmxdevice ∗ d e v i c e ,
struct w s t a t e ∗ p r e v s t a t e ,
303
int i n t s r c
)
{
305
/∗ we ’ r e v e r y p a t i e n t a g a i n f o r d a t a ∗/
d e v i c e −>p a t i e n c e = DMX_TO_PATIENCE;
307
/∗ The p e r i p h e r a l end f l a g i s a u t o m a t i c a l l y c l e a r e d when one o f t h e c o u n t e r −r e g i s t e r s (
Counter or
∗ Next Counter R e g i s t e r ) i s w r i t t e n . ∗/
309
UART_PUT_RNCR( d e v i c e , 0 ) ;
UART_PUT_CR( d e v i c e , AT91_US_RSTSTA) ;
311
/∗ u p d a t e i n t e r r u p t c o n f i g u r a t i o n ∗/
UART_PUT_IDR( d e v i c e , ~( d e v i c e −>s t a t e −>e v e n t s ) ) ;
313
UART_PUT_IER( d e v i c e ,
d e v i c e −>s t a t e −>e v e n t s ) ;
return ;
315 }
s t a t i c void f b u f _ s e r v i c e ( struct dmxdevice ∗ d e v i c e ,
317
struct w s t a t e ∗ r e q s t a t e ,
int i n t s r c
)
319 {
struct w s t a t e ∗ n e x t s t a t e = 0 ;
321
struct dmxbuffer ∗ nbuf ;
int d a t a d e l t a = 0 ;
323
i n t count = 0 ;
i f ( i n t s r c & AT91_US_TIMEOUT) {
325
UART_PUT_CR( d e v i c e , AT91_US_RETTO | AT91_US_STTTO) ;
d e v i c e −>p a t i e n c e −−;
327
}
/∗ s u c c e s f u l r e a d ? ∗/
329
count = UART_GET_RCR( d e v i c e ) ;
if
( ( ( i n t s r c & AT91_US_FRAME) == 0 )
331
&& ( i n t s r c & (AT91_US_RXBRK | AT91_US_ENDRX) )
) {
/∗ d i s a b l e dma t r a n s f e r ∗/
333
UART_PUT_PTCR( d e v i c e , AT91_PDC_RXTDIS) ;
99
335
337
339
341
343
345
347
349
351
353
355
357
359
361
363
365
367
369
371
373
375
377
379
381
383
385
387
389
391
393
395
397
399
dma_unmap_single ( d e v i c e −>dev , d e v i c e −>wbuf−>dma_addr ,
5 1 3 , DMA_FROM_DEVICE) ;
/∗ u p d a t e l e n g t h f i e l d o f d a t a b u f f e r ∗/
d e v i c e −>wbuf−>l e n = 1+(512 − count ) ;
p r i n t k ( KERN_DEBUG "DMX%i : g o t data (% i ) \n" ,
i d e n t d e v ( d e v i c e ) , d e v i c e −>wbuf−>l e n −1) ;
/∗ i n c a s e o f a l i n e b r e a k , s k i p w l b s t a t e ∗/
i f ( i n t s r c & AT91_US_RXBRK)
n e x t s t a t e = &wsc ;
else
n e x t s t a t e = &wlb ;
d e v i c e −>wbuf−>t o u c h e d = 0 ;
d e v i c e −>s i l e n c e = 0 ;
/∗ i f p o s s i b l e s w i t c h b u f f e r s ∗/
nbuf = n e x t b u f f e r ( d e v i c e ) ;
s p i n _ l o c k (& d e v i c e −>wbuf−>l o c k ) ;
i f ( s p i n _ t r y l o c k (&nbuf−>l o c k ) ) {
/∗ compare o l d and new data , s t a r t w i t h c h a n n e l 1 ∗/
for (
datadelta = 1;
/∗ No p o i n t i n comparing i f l e n g t h s d i f f e r ∗/
( ( d e v i c e −>wbuf−>l e n == nbuf−>l e n )
/∗ q u i t l o o p when p a s s e d end o f b u f f e r ∗/
&& ( d a t a d e l t a < ( d e v i c e −>wbuf−>l e n +1) )
/∗ a l s o q u i t i f v a l u e s a t c u r r e n t o f f s e t d i f f e r ∗/
&& ( nbuf−>data [ d a t a d e l t a ] == d e v i c e −>wbuf−>data [ d a t a d e l t a ] )
);
d a t a d e l t a++
);
/∗ i f l o o p q u i t b e f o r e p a s s i n g t h e end , u n i v e r s e changed ∗/
i f ( ( d a t a d e l t a != ( d e v i c e −>wbuf−>l e n ) ) | | d e v i c e −>s i l e n c e ) {
/∗ s w i t c h b u f f e r s ∗/
d e v i c e −>i b u f = d e v i c e −>wbuf ;
d e v i c e −>wbuf = nbuf ;
/∗ n o t i f y p r o c e s s e s ∗/
w a k e _ u p _ i n t e r r u p t i b l e (& d e v i c e −>i b u f q ) ;
}
/∗ f i r s t u n l o c k our o l d wbuf ∗/
s p i n _ u n l o c k (& d e v i c e −>i b u f −>l o c k ) ;
}
/∗ u n l o c k our new wbuf ∗/
s p i n _ u n l o c k (& d e v i c e −>wbuf−>l o c k ) ;
/∗ u p d a t e s t a t ’ s ∗/
d e v i c e −>r x c ++;
i f ( ! d e v i c e −>wbuf−>t o u c h e d )
d e v i c e −>o v e r f ++;
}
/∗ e x c e p t i o n s : l e a v e t h e s t a t e p r e m a t u r e l y ∗/
if (
( ( d e v i c e −>p a t i e n c e == 0 ) ) // && ( c o u n t == 512) )
||
( i n t s r c & AT91_US_FRAME )
||
( ! intsrc
)
){
/∗ d i s a b l e t h e DMA t r a n s f e r ∗/
UART_PUT_PTCR( d e v i c e , AT91_PDC_RXTDIS) ;
dma_unmap_single ( d e v i c e −>dev , d e v i c e −>wbuf−>dma_addr ,
5 1 3 , DMA_FROM_DEVICE) ;
i f ( ! d e v i c e −>p a t i e n c e )
p r i n t k (KERN_DEBUG "DMX%i : FBUF TIMEOUT\n" , i d e n t d e v ( d e v i c e ) ) ;
i f ( i n t s r c & AT91_US_FRAME)
p r i n t k (KERN_DEBUG "DMX%i : FRAMEERR\n" , i d e n t d e v ( d e v i c e ) ) ;
/∗ n o t s e r v i c i n g an i n t e r r u p t . a b o r t s t a t e ∗/
if (! intsrc )
return ;
n e x t s t a t e = &wlb ;
}
if ( nextstate )
t o s t a t e ( device , nextstate , i n t s r c ) ;
401 }
/∗++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++∗/
403 /∗ t l b − Transmit l i n e b r e a k ∗/
100
405
407
409
411
413
415
417
419
421
423
425
427
429
431
433
435
437
439
441
443
445
447
449
451
453
455
457
459
461
463
465
467
469
471
473
s t a t i c void t l b _ s e t u p ( struct dmxdevice ∗ d e v i c e ,
struct w s t a t e ∗ p r e v s t a t e ,
int i n t s r c
)
{
struct dmxbuffer ∗ nbuf ;
i f ( p r e v s t a t e != &t b u f ) {
/∗ u s e r has l o a d e d i b u f f o r t h e worker when ∗
∗ t h i s code k i c k s in , so s w i t c h b u f f e r s
∗/
nbuf = n e x t b u f f e r ( d e v i c e ) ;
s p i n _ l o c k (& d e v i c e −>wbuf−>l o c k ) ;
s p i n _ l o c k (&nbuf−>l o c k ) ;
/∗ Perform t h e swap ∗/
d e v i c e −>i b u f = d e v i c e −>wbuf ;
d e v i c e −>wbuf = nbuf ;
/∗ r e l e a s e b u f f e r s ∗/
s p i n _ u n l o c k (& d e v i c e −>i b u f −>l o c k ) ;
s p i n _ u n l o c k (& d e v i c e −>wbuf−>l o c k ) ;
/∗ Enable t r a n s c e i v e r , i f p i n p r e s e n t ∗/
i f ( ( ( struct at91_uart_data ∗ ) d e v i c e −>dev−>p l a t f o r m _ d a t a )−>r t s . p i n )
gpio_set_output (& d e v i c e −>g p i o , ( ( struct at91_uart_data ∗ )
d e v i c e −>dev−>p l a t f o r m _ d a t a )−>r t s . pin , 1 ) ;
}
/∗ p u t UART i n l o c a l l o o p ∗/
UART_PUT_MR( d e v i c e , DMX_MR_VAL | AT91_US_CHMODE_LOC_LOOP) ;
/∗ TxD p i n low ∗/
gpio_set_output (& d e v i c e −>g p i o , ( ( struct at91_uart_data ∗ )
d e v i c e −>dev−>p l a t f o r m _ d a t a )−>txd . pin , 0 ) ;
/∗ TxD p i n i n GPIO mode ∗/
g p i o _ s e t _ c o n f i g (& d e v i c e −>g p i o , ( ( struct at91_uart_data ∗ )
d e v i c e −>dev−>p l a t f o r m _ d a t a )−>txd . pin ,
AT91_GPIO_PIOOUT) ;
/∗ Map t h e DMA and s t a r t o f f w i t h t h r e e dummies ∗/
d e v i c e −>wbuf−>dma_addr = dma_map_single ( d e v i c e −>dev ,
d e v i c e −>wbuf−>data ,
5 1 3 , DMA_TO_DEVICE) ;
UART_PUT_TPR( d e v i c e , d e v i c e −>wbuf−>dma_addr ) ;
UART_PUT_TNPR( d e v i c e , 0 ) ;
UART_PUT_TCR( d e v i c e , 3 ) ;
UART_PUT_PTCR( d e v i c e , AT91_PDC_TXTEN) ;
d e v i c e −>s i l e n c e = 0 ;
// u p d a t e i n t e r r u p t c o n f i g u r a t i o n
UART_PUT_IDR( d e v i c e , ~( d e v i c e −>s t a t e −>e v e n t s ) ) ;
UART_PUT_IER( d e v i c e ,
d e v i c e −>s t a t e −>e v e n t s ) ;
}
s t a t i c void t l b _ s e r v i c e ( struct dmxdevice ∗ d e v i c e ,
struct w s t a t e ∗ r e q s t a t e ,
int i n t s r c
)
{
/∗ d i s a b l e PDC t r a n s m i t ∗/
UART_PUT_PTCR( d e v i c e , AT91_PDC_TXTDIS) ;
if ( intsrc ) {
t o s t a t e ( d e v i c e , &tmab , i n t s r c ) ;
} else {
/∗ unmap t h e DMA ∗/
dma_unmap_single ( d e v i c e −>dev , d e v i c e −>wbuf−>dma_addr ,
5 1 3 , DMA_TO_DEVICE) ;
/∗ d i s a b l e UART l o c a l l o o p ∗/
UART_PUT_MR( d e v i c e , DMX_MR_VAL) ;
/∗ TxD p i n i n P e r i p h e r a l mode ∗/
g p i o _ s e t _ c o n f i g (& d e v i c e −>g p i o , ( ( struct at91_uart_data ∗ )
d e v i c e −>dev−>p l a t f o r m _ d a t a )−>txd . pin ,
( ( struct at91_uart_data ∗ )
d e v i c e −>dev−>p l a t f o r m _ d a t a )−>txd . c f g ) ;
/∗ D i s a b l e t r a n s c e i v e r , i f p i n p r e s e n t ∗/
i f ( ( ( struct at91_uart_data ∗ ) d e v i c e −>dev−>p l a t f o r m _ d a t a )−>r t s . p i n )
gpio_set_output (& d e v i c e −>g p i o , ( ( struct at91_uart_data ∗ )
d e v i c e −>dev−>p l a t f o r m _ d a t a )−>r t s . pin , 0 ) ;
}
}
/∗ tmab − Transmit mark−a f t e r b r e a k ∗/
Code Listings
101
s t a t i c void tmab_setup ( struct dmxdevice ∗ d e v i c e ,
struct w s t a t e ∗ p r e v s t a t e ,
int i n t s r c
)
477 {
/∗ TxD h i g h ∗/
479
gpio_set_output (& d e v i c e −>g p i o , ( ( struct at91_uart_data ∗ )
d e v i c e −>dev−>p l a t f o r m _ d a t a )−>txd . pin , 1 ) ;
481
/∗ u p d a t e i n t e r r u p t c o n f i g u r a t i o n ∗/
483
UART_PUT_IDR( d e v i c e , ~( d e v i c e −>s t a t e −>e v e n t s ) ) ;
UART_PUT_IER( d e v i c e ,
d e v i c e −>s t a t e −>e v e n t s ) ;
485 }
s t a t i c void t m ab _ s e rv i c e ( struct dmxdevice ∗ d e v i c e ,
487
struct w s t a t e ∗ r e q s t a t e ,
int i n t s r c
)
489 {
/∗ d i s a b l e PDC t r a n s m i t ∗/
491
UART_PUT_PTCR( d e v i c e , AT91_PDC_TXTDIS) ;
/∗ d i s a b l e UART l o c a l l o o p ∗/
493
UART_PUT_MR( d e v i c e , DMX_MR_VAL) ;
/∗ TxD p i n i n P e r i p h e r a l mode ∗/
495
g p i o _ s e t _ c o n f i g (& d e v i c e −>g p i o , ( ( struct at91_uart_data ∗ )
d e v i c e −>dev−>p l a t f o r m _ d a t a )−>txd . pin ,
497
( ( struct at91_uart_data ∗ )
d e v i c e −>dev−>p l a t f o r m _ d a t a )−>txd . c f g ) ;
499
if (! intsrc ) {
/∗ a l s o unmap t h e DMA ∗/
501
dma_unmap_single ( d e v i c e −>dev ,
d e v i c e −>wbuf−>dma_addr ,
503
5 1 3 , DMA_TO_DEVICE) ;
/∗ D i s a b l e t r a n s c e i v e r , i f p i n p r e s e n t ∗/
505
i f ( ( ( struct at91_uart_data ∗ )
d e v i c e −>dev−>p l a t f o r m _ d a t a )−>r t s . p i n )
507
gpio_set_output (& d e v i c e −>g p i o , ( ( struct at91_uart_data ∗ )
d e v i c e −>dev−>p l a t f o r m _ d a t a )−>r t s . pin , 0 ) ;
509
return ;
}
511
i f ( i n t s r c & AT91_US_TXEMPTY) // AT91_US_ENDTX)
t o s t a t e ( d e v i c e , &t b u f , i n t s r c ) ;
513 }
/∗ t b u f − Transmit dmx frame ∗/
515 s t a t i c void t b u f _ s e t u p ( struct dmxdevice ∗ d e v i c e ,
struct w s t a t e ∗ p r e v s t a t e ,
517
int i n t s r c
)
{
519
int n ;
/∗ send minimum 96 c h a n n e l s ∗/
521
n = d e v i c e −>wbuf−>l e n ;
n = ( n < 9 7 ) ? 97 : n ;
523
/∗ have t h e PDC send t h e b u f f e r ( i n c l u d e s SC) ∗/
UART_PUT_TPR( d e v i c e , d e v i c e −>wbuf−>dma_addr ) ;
525
UART_PUT_TNPR( d e v i c e , 0 ) ;
UART_PUT_TCR( d e v i c e , n ) ;
527
UART_PUT_PTCR( d e v i c e , AT91_PDC_TXTEN) ;
/∗ u p d a t e i n t e r r u p t c o n f i g u r a t i o n ∗/
529
UART_PUT_IDR( d e v i c e , ~( d e v i c e −>s t a t e −>e v e n t s ) ) ;
UART_PUT_IER( d e v i c e ,
d e v i c e −>s t a t e −>e v e n t s ) ;
531 }
s t a t i c void t b u f _ s e r v i c e ( struct dmxdevice ∗ d e v i c e ,
533
struct w s t a t e ∗ r e q s t a t e ,
int i n t s r c
)
535 {
struct dmxbuffer ∗ nbuf ;
537
/∗ d i s a b l e pdc t r a n s m i t ∗/
UART_PUT_PTCR( d e v i c e , AT91_PDC_TXTDIS) ;
539
dma_unmap_single ( d e v i c e −>dev ,
d e v i c e −>wbuf−>dma_addr ,
541
5 1 3 , DMA_TO_DEVICE) ;
if (! intsrc ) {
543
/∗ D i s a b l e t r a n s c e i v e r , i f p i n p r e s e n t ∗/
475
102
Code Listings
(!(
( struct at91_uart_data ∗ )
d e v i c e −>dev−>p l a t f o r m _ d a t a )−>r t s . p i n )
return ;
gpio_set_output (& d e v i c e −>g p i o ,
( ( struct at91_uart_data ∗ )
d e v i c e −>dev−>p l a t f o r m _ d a t a )−>r t s . pin ,
0) ;
return ;
if
545
547
549
551
553
555
557
559
561
563
565
567
569
571
573
}
p r i n t k ( KERN_DEBUG "DMX%i : Frame s e n t \n" ,
identdev ( device ) ) ;
d e v i c e −>t x c ++;
d e v i c e −>wbuf−>t o u c h e d = 0 ;
/∗ s w i t c h b u f f e r s i f p o s s i b l e ∗/
nbuf = n e x t b u f f e r ( d e v i c e ) ;
i f ( s p i n _ t r y l o c k (&nbuf−>l o c k ) ) {
/∗ new d a t a ? u s e i t ! ∗/
i f ( nbuf−>t o u c h e d ) {
d e v i c e −>i b u f = d e v i c e −>wbuf ;
d e v i c e −>wbuf = nbuf ;
}
s p i n _ u n l o c k (&nbuf−>l o c k ) ;
}
i f ( d e v i c e −>s i l e n c e ) {
d e v i c e −>s i l e n c e = 0 ;
t o s t a t e ( d e v i c e ,& i d l e s , i n t s r c ) ;
} else {
/∗ go work on n e x t b u f f e r ∗/
t o s t a t e ( d e v i c e ,& t l b , i n t s r c ) ;
}
w a k e _ u p _ i n t e r r u p t i b l e (& d e v i c e −>i b u f q ) ;
575 }
/∗++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++∗/
577 /∗ I n t e r r u p t h a n d l e r ∗/
s t a t i c i r q r e t u r n _ t dmx_interrupt ( i n t i r q , void ∗ dev_id ,
579
struct pt _r eg s ∗ r e g s
)
{
581
struct dmxdevice ∗ d e v i c e = dev_id ;
unsigned i n t i n t s r c = UART_GET_CSR( d e v i c e ) ;
583
struct w s t a t e ∗ s t a t e = d e v i c e −>s t a t e ;
585
587
589
591
593
barrier () ;
BUG_ON( ! s t a t e ) ;
i f ( u n l i k e l y ( ! i n t s r c & s t a t e −>e v e n t s ) )
p r i n t k ( KERN_DEBUG "DMX%i : ! e x p e c t e d INT : %x @%s \n" ,
identdev ( device ) ,
i n t s r c & ~( s t a t e −>e v e n t s ) ,
s t a t e −>d e s c r i p t i o n ) ;
i f ( i n t s r c & s t a t e −>e v e n t s )
s t a t e −>s e r v i c e ( d e v i c e , 0 , i n t s r c & s t a t e −>e v e n t s ) ;
return IRQ_HANDLED;
595 }
/∗++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++∗/
597 /∗ C h a r a c t e r d e v i c e i m p l e m e n t a t i o n ∗/
s t a t i c i n t dmx_cdev_open ( struct i n o d e ∗ i n o d e , struct f i l e ∗ f i l e )
599 {
struct dmxdevice ∗ d e v i c e ;
601
int i = 0 ;
/∗ d e t e r m i n e UART number by s u b s t r a c t i n g iminor by MINOR_START ∗/
603
i = iminor ( inode ) ;
i f ( i < MINOR_START)
605
return −ENODEV;
i = i − MINOR_START;
607
/∗ o n l y 4 UARTS ∗/
i f ( i >3)
609
return −ENODEV;
d e v i c e = &( dmxdevices [ i ] ) ;
611
p r i n t k (KERN_DEBUG "DMX%i : open ( ) \n" , i d e n t d e v ( d e v i c e ) ) ;
/∗ a b o r t i f t h e d e v i c e d i d n ’ t p a s s p r o b e ( ) ∗/
613
i f ( ! d e v i c e −>s t a t e )
103
615
617
619
return −ENODEV;
/∗ t h e o t h e r f u n c t i o n s o n l y g e t t h e f i l e
f i l e −>p r i v a t e _ d a t a = d e v i c e ;
i f ( d e v i c e −>s t a t e != &i d l e s )
t o s t a t e ( d e v i c e ,& i d l e s , 0 ) ;
return 0 ;
s t r u c t , so p u t a p o i n t e r i n t h e r e ∗/
}
621 s t a t i c i n t dmx_cdev_release ( struct i n o d e ∗ i n o d e , struct f i l e ∗ f i l e )
{
623
struct w s t a t e ∗ c s t a t e ;
struct dmxdevice ∗ d e v i c e = ( struct dmxdevice ∗ ) f i l e −>p r i v a t e _ d a t a ;
625
barrier () ;
627
p r i n t k (KERN_DEBUG "DMX%i : r e l e a s e ( ) \n" , i d e n t d e v ( d e v i c e ) ) ;
i f ( ! ( f i l e −>f _ f l a g s & O_NONBLOCK) ) {
629
c s t a t e = d e v i c e −>s t a t e ;
i f ( ( ( c s t a t e == &t l b ) | | ( c s t a t e == &tmab ) | | ( c s t a t e == &t b u f ) ) ) {
631
i f ( d e v i c e −>s t a t e != &i d l e s ) {
/∗ w a i t u n t i l b l o c k i n g w r i t e i s c o m p l e t e ∗/
633
d e v i c e −>s i l e n c e = 1 ;
p r i n t k ( KERN_DEBUG "DMX%i : h o l d i n g back r e l e a s e ( ) t o c o m p l e t e w r i t e . . . \ n" ,
635
identdev ( device ) ) ;
i f ( w a i t _ e v e n t _ i n t e r r u p t i b l e ( d e v i c e −>i b u f q ,
637
d e v i c e −>s i l e n c e == 0 ) ) {
t o s t a t e ( d e v i c e , &i d l e s , 0 ) ;
639
return −ERESTARTSYS;
}
641
}
}
643
}
t o s t a t e ( d e v i c e , &i d l e s , 0 ) ;
645
return 0 ;
}
647 s t a t i c s s i z e _ t dmx_cdev_write ( struct f i l e ∗ f i l e , const char __user ∗ b u f f e r ,
s i z e _ t count , l o f f _ t ∗ ppos )
649 {
struct dmxdevice ∗ d e v i c e = ( struct dmxdevice ∗ ) f i l e −>p r i v a t e _ d a t a ;
651
struct dmxbuffer ∗ i b u f ;
struct w s t a t e ∗ c s t a t e = d e v i c e −>s t a t e ;
653
ssize_t retval = 0;
i n t l e n = count > 512 ? 512 : count ;
655
barrier () ;
657
p r i n t k (KERN_DEBUG "DMX%i : Write(% i ) \n" , i d e n t d e v ( d e v i c e ) , count ) ;
/∗ s w i t c h t o TX mode ∗/
659
i f ( ( c s t a t e != &t l b ) && ( c s t a t e != &tmab )
&& ( c s t a t e != &t b u f ) && ( c s t a t e != &i d l e s ) ) {
661
UART_PUT_IDR( d e v i c e , −1) ;
t o s t a t e ( d e v i c e ,& i d l e s , 0 ) ;
663
}
while ( ( ! d e v i c e −>i b u f )
665
||
( ! s p i n _ t r y l o c k (& d e v i c e −>i b u f −>l o c k ) ) ) {
/∗ non−b l o c k i n g r e q u e s t , r e t u r n i m e d i a t e l y ∗/
667
i f ( f i l e −>f _ f l a g s & O_NONBLOCK)
return −EAGAIN ;
669
p r i n t k ( KERN_DEBUG "DMX%i : w a i t i n g f o r b u f f e r . . . \ n" ,
identdev ( device ) ) ;
671
i f ( w a i t _ e v e n t _ i n t e r r u p t i b l e ( d e v i c e −>i b u f q , d e v i c e −>i b u f > 0 ) )
/∗ we woke up b e c a u s e o f a s i g n a l ∗/
673
return −ERESTARTSYS;
}
675
i b u f = d e v i c e −>i b u f ;
i f ( copy_from_user ( ( i b u f −>data + s i z e o f ( char ) ) , b u f f e r , l e n ) ) {
677
r e t v a l = −EFAULT;
} else {
679
/∗ don ’ t o v e r w r i t e a b u f f e r t h a t ’ s n o t been send ∗/
i f ( i b u f −>t o u c h e d ) {
681
s p i n _ u n l o c k (& i b u f −>l o c k ) ;
return −EAGAIN ;
683
} else {
104
685
687
689
691
693
695
697
Code Listings
i b u f −>t o u c h e d = 1 ;
}
i f ( i b u f −>l e n > count +1)
/∗ z e r o t h e −now unused− u n i v e r s e s ∗/
memset(& i b u f −>data [ count +1] , 0 , i b u f −>l e n − ( count +1) ) ;
/∗ make f i r s t b y t e o f b u f f e r SC ∗/
i b u f −>data [ 0 ] = 0 ;
i b u f −>l e n = count + 1 ;
retval = len ;
s p i n _ u n l o c k (& i b u f −>l o c k ) ;
i f ( d e v i c e −>s t a t e == &i d l e s )
/∗ s t a r t t r a n s m i t t i n g ∗/
t o s t a t e ( d e v i c e , &t l b , 0 ) ;
}
return r e t v a l ;
699 }
s t a t i c s s i z e _ t dmx_cdev_read ( struct f i l e ∗ f i l e , char __user ∗ b u f f e r ,
701
s i z e _ t count , l o f f _ t ∗ ppos
)
{
703
struct dmxdevice ∗ d e v i c e = ( struct dmxdevice ∗ ) f i l e −>p r i v a t e _ d a t a ;
struct w s t a t e ∗ c s t a t e = d e v i c e −>s t a t e ;
705
struct dmxbuffer ∗ i b u f ;
ssize_t retval ;
707
/∗ c a t w i l l i s s u e a 8 k read , so t r i m t h a t down ∗/
i n t l e n = count > 512 ? 512 : count ;
709
barrier () ;
p r i n t k (KERN_DEBUG "DMX%i : Read(% i ) \n" , i d e n t d e v ( d e v i c e ) , count ) ;
711
/∗ s w i t c h t o RX mode ∗/
i f ( ( c s t a t e != &wlb ) && ( c s t a t e != &wsc ) && ( c s t a t e != &f b u f ) ) {
713
i f ( c s t a t e != &i d l e s ) {
UART_PUT_IDR( d e v i c e , −1) ;
715
t o s t a t e ( d e v i c e ,& i d l e s , 0 ) ;
}
717
t o s t a t e ( d e v i c e ,& wlb , 0 ) ;
}
719
/∗ Wait as l o n g as we can ’ t g e t a l o c k on a b u f f e r ∗/
while ( ( d e v i c e −>i b u f != ( struct dmxbuffer ∗ ) −1)
721
&& ( ( ! d e v i c e −>i b u f ) | | ( ! s p i n _ t r y l o c k (& d e v i c e −>i b u f −>l o c k ) ) )
) {
723
i f ( f i l e −>f _ f l a g s & O_NONBLOCK)
/∗ non−b l o c k i n g r e q u e s t , r e t u r n i m e d i a t e l y ∗/
725
return −EAGAIN ;
p r i n t k ( KERN_DEBUG "DMX%i : Read ( ) − w a i t i n g f o r data . . . \ n" ,
727
identdev ( device ) ) ;
i f ( ! d e v i c e −>i b u f )
729
i f ( w a i t _ e v e n t _ i n t e r r u p t i b l e ( d e v i c e −>i b u f q , d e v i c e −>i b u f ) )
/∗ we woke up b e c a u s e o f a s i g n a l ∗/
731
return −ERESTARTSYS;
}
733
i f ( d e v i c e −>i b u f == ( struct dmxbuffer ∗ ) −1) {
/∗ mark i b u f ∗ as ’ no new d a t a ’ ∗/
735
d e v i c e −>i b u f = ( struct dmxbuffer ∗ ) 0 ;
return 0 ;
737
} else {
i b u f = d e v i c e −>i b u f ;
739
i f ( d e v i c e −>s i l e n c e ) {
/∗ r e t u r n n o t h i n g (EOF) ∗/
741
retval = 0;
} else {
743
/∗ mark i b u f as ’ no new d a t a ’ ∗/
i b u f −>t o u c h e d = 1 ;
745
/∗ p a s s on d a t a t o u s e r s p a c e ∗/
l e n = i b u f −>l e n − 1 ;
747
i f ( copy_to_user ( b u f f e r , i b u f −>data + s i z e o f ( char ) , l e n ) )
r e t v a l = −EFAULT;
749
else
retval = len ;
751
}
/∗ &i b u f = 0 −−> ’ no b u f f e r a v a i l a b l e now ’ ∗/
753
d e v i c e −>i b u f = ( struct dmxbuffer ∗ ) 0 ;
105
755
s p i n _ u n l o c k (& i b u f −>l o c k ) ;
}
return r e t v a l ;
757 }
s t a t i c i n t dmx_cdev_ioctl ( struct i n o d e ∗ i n o d e , struct f i l e ∗ f i l e ,
759
unsigned i n t cmd , unsigned long a r g )
{
761
/∗ t h i s f u n c t i o n i s a p l a c e h o l d e r f o r now ∗/
return −ENOIOCTLCMD;
763 }
s t a t i c unsigned i n t dmx_cdev_poll ( struct f i l e ∗ f i l e ,
765
struct p o l l _ t a b l e _ s t r u c t ∗ t a b l e )
{
767
struct dmxdevice ∗ d e v i c e = ( struct dmxdevice ∗ ) f i l e −>p r i v a t e _ d a t a ;
unsigned i n t r e t v a l = 0 ;
769
struct w s t a t e ∗ c s t a t e = d e v i c e −>s t a t e ;
barrier () ;
771
p r i n t k (KERN_DEBUG "DMX%i : p o l l ( ) \n" , i d e n t d e v ( d e v i c e ) ) ;
/∗ r e g i s t e r my w a i t q u e u e w i t h t h e p o l l t a b l e ∗/
773
p o l l _ w a i t ( f i l e , &d e v i c e −>i b u f q , t a b l e ) ;
i f ( ( c s t a t e == &wlb ) | | ( c s t a t e == &wsc ) | | ( c s t a t e == &f b u f ) ) {
775
/∗ t h i s p o r t i s r e c e i v i n g DMX d a t a ∗/
/∗ s w i t c h i n g t o TX can be done i n RX mode ∗/
777
r e t v a l |= (POLLOUT | POLLWRNORM) ;
/∗ i s t h e r e : a ) s i l e n c e t o s i g n a l t o u s e r s p a c e
779
∗
b ) an un touche d d a t a b u f f e r ∗/
i f ( ( d e v i c e −>i b u f == ( struct dmxbuffer ∗ ) −1)
781
||
( ( d e v i c e −>i b u f != ( struct dmxbuffer ∗ ) 0 )
&& ( d e v i c e −>i b u f −>t o u c h e d == 0 )
))
783
r e t v a l |= (POLLIN | POLLRDNORM) ;
785
787
789
791
793
795
797
} else i f (
( c s t a t e == &i d l e s ) | | ( c s t a t e == &t l b )
||
( c s t a t e == &tmab ) | | ( c s t a t e == &t b u f ) ) {
/∗ t h i s p o r t i s s e n d i n g DMX d a t a ∗/
/∗ s w i t c h i n g t o RX can be done i n TX mode ∗/
r e t v a l |= (POLLIN | POLLRDNORM) ;
/∗ can t h e u n i v e r s e d a t a be u p d a t e d ? ( new b u f f e r f i l l e d ) ∗/
i f ( ( d e v i c e −>i b u f != ( struct dmxbuffer ∗ ) 0 )
&& ( d e v i c e −>i b u f != ( struct dmxbuffer ∗ ) −1)
/∗ s i l e n c e==1 i n TX mode means go i d l e a f t e r c u r r e n t send ∗/
&& ( d e v i c e −>s i l e n c e == 0 ) )
r e t v a l |= (POLLOUT | POLLWRNORM) ;
}
return r e t v a l ;
}
799 /∗++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++∗/
/∗ p r o c f i l e s y s t e m e n t r y ∗/
801 s t a t i c i n t dmx_proc_read ( char ∗ buf , char ∗∗ s t a r t , o f f _ t o f f s e t ,
i n t l e n , i n t ∗ unused_i , void ∗ unused_v )
803 {
int i , l ;
805
char ∗ d s t ;
struct dmxdevice ∗ d e v i c e ;
807
struct w s t a t e ∗ s t a t e ;
809
811
813
815
817
819
821
823
dst = buf ;
f o r ( i =0; i <4; i ++) {
d e v i c e = &dmxdevices [ i ] ;
s t a t e = d e v i c e −>s t a t e ;
l = s n p r i n t f ( dst , l e n , "DMX%i : s t a t e=%s " , i ,
s t a t e ? s t a t e −>d e s c r i p t i o n : " a b s e n t : \ n" ) ;
d s t+=l ; l e n−=l ;
if (! state )
break ;
l = s n p r i n t f ( dst , l e n , " ; s i l e n c e=%d" , d e v i c e −>s i l e n c e ) ;
d s t+=l ; l e n−=l ;
l = s n p r i n t f ( dst , l e n , " ; f e r r=%d" ,
d e v i c e −>f e r r ) ;
d s t+=l ; l e n−=l ;
l = s n p r i n t f ( dst , l e n , " ; o v e r f=%d" , d e v i c e −>o v e r f ) ;
d s t+=l ; l e n−=l ;
l = s n p r i n t f ( dst , l e n , " ; t x c=%d" , d e v i c e −>t x c ) ;
d s t+=l ; l e n−=l ;
l = s n p r i n t f ( dst , l e n , " ; r x c=%d" , d e v i c e −>r x c ) ;
d s t+=l ; l e n−=l ;
l = s n p r i n t f ( dst , l e n , " ; i r q=%d" , d e v i c e −>i r q ) ;
d s t+=l ; l e n−=l ;
106
825
827
829
831
Code Listings
l
l
l
l
l
=
=
=
=
=
s n p r i n t f ( dst
s n p r i n t f ( dst
s n p r i n t f ( dst
s n p r i n t f ( dst
s n p r i n t f ( dst
,
,
,
,
,
len
len
len
len
len
,
,
,
,
,
" ; p a t i e n c e=%i " ,
d e v i c e −>p a t i e n c e ) ;
d s t+=l ; l e n−=l ;
" ; u a r t c l k=%i " , d e v i c e −>u a r t c l k ) ;
d s t+=l ; l e n−=l ;
" ; d i v i d e r=%i " , d e v i c e −>d i v i d e r ) ;
d s t+=l ; l e n−=l ;
" ; membase=0x%x" , ( i n t ) d e v i c e −>membase ) ; d s t+=l ; l e n−=l ;
" ; mapbase=0x%x : \ n" , ( i n t ) d e v i c e −>mapbase ) ; d s t+=l ; l e n−=l ;
}
/∗ r e t u r n d i f f e r e n c e b e t w e e n w o r k p o i n t e r and b e g i n p o i n t e r ∗/
return d s t − b u f ;
}
833 /∗++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++∗/
/∗ P l a t f o r m D e v i c e i m p l e m e n t a t i o n and low l e v e l i n i t i a l i s a t i o n ∗/
835 /∗ dmx_probe − I n i t i a l i z e d e v i c e from p l a t f o r m d e v i c e r e s o u r c e ∗/
s t a t i c i n t __devinit dmx_probe ( struct p l a t f o r m _ d e v i c e ∗ pdev )
837 {
struct dmxdevice ∗ d e v i c e = &( dmxdevices [ pdev−>i d ] ) ;
839
struct at91_uart_data ∗ uart_data = pdev−>dev . p l a t f o r m _ d a t a ;
int s i z e , r e t v a l ;
841
i n t i , baud = 2 5 0 0 0 0 ;
843
845
847
849
851
853
855
857
859
861
863
865
867
869
871
873
875
877
879
881
883
885
887
889
891
893
d e v i c e −>dev = &pdev−>dev ;
d e v i c e −>mapbase = pdev−>r e s o u r c e [ 0 ] . s t a r t ;
d e v i c e −>i r q
= pdev−>r e s o u r c e [ 1 ] . s t a r t ;
d e v i c e −>s t a t e = ( struct w s t a t e ∗ ) 0 x00 ;
d e v i c e −>g p i o . name = pdev−>dev . bus_id ? pdev−>dev . bus_id : pdev−>name ;
/∗ c l k _ g e t − o b t a i n a r e f e r e n c e t o a c l o c k p r o d u c e r ∗/
d e v i c e −>c l k = c l k _ g e t (&pdev−>dev , uart_data−>clk_name ) ;
c l k _ e n a b l e ( d e v i c e −>c l k ) ;
/∗ c l k _ g e t _ r a t e − o b t a i n c u r r e n t c l o c k r a t e ( Hz ) ∗/
d e v i c e −>u a r t c l k = c l k _ g e t _ r a t e ( d e v i c e −>c l k ) ;
i f ( g p i o _ a t t a c h _ c l i e n t (& d e v i c e −>g p i o ) ) {
p r i n t k (KERN_ERR "DMX%i : GPIO c l i e n t c o u l d not a t t a c h ! \ n" , pdev−>i d ) ;
goto c l k _ e r r o r ;
}
i f ( g p i o _ a c q u i r e _ c f g (& d e v i c e −>g p i o , uart_data−>txd . pin , uart_data−>txd . c f g ) ) {
p r i n t k (KERN_ERR "DMX%i : Could not s e t u p Txd Pin ! \ n" , pdev−>i d ) ;
goto p i n _ e r r o r ;
}
i f ( g p i o _ a c q u i r e _ c f g (& d e v i c e −>g p i o , uart_data−>rxd . pin , uart_data−>rxd . c f g ) ) {
p r i n t k (KERN_ERR "DMX%i : Could not s e t u p Rxd Pin ! \ n" , pdev−>i d ) ;
goto p i n _ e r r o r ;
}
i f ( uart_data−>r t s . p i n ) {
i f ( g p i o _ a c q u i r e _ c f g (& d e v i c e −>g p i o , uart_data−>r t s . pin , AT91_GPIO_PIOOUT) ) {
p r i n t k (KERN_ERR "DMX%i : Could not s e t u p RTS Pin ! \ n" , pdev−>i d ) ;
goto p i n _ e r r o r ;
}
}
/∗ R e q u e s t t h e memory r e g i o n ( s ) b e i n g used . ∗/
s i z e = pdev−>r e s o u r c e [ 0 ] . end − pdev−>r e s o u r c e [ 0 ] . s t a r t + 1 ;
i f ( ! request_mem_region ( d e v i c e −>mapbase , s i z e , "at91_dmx" ) ) {
p r i n t k (KERN_ERR "DMX%i : request_mem ( ) f a i l e d ! \ n" , pdev−>i d ) ;
goto requestmem_error ;
}
d e v i c e −>membase = ioremap ( d e v i c e −>mapbase , s i z e ) ;
i f ( d e v i c e −>membase == NULL) {
p r i n t k (KERN_ERR "DMX%i : ioremap f a i l e d ! \ n" , pdev−>i d ) ;
goto map_error ;
}
/∗ S e t u p d a t a b u f f e r s ∗/
f o r ( i = 0 ; i < 2 ; i ++) {
s p i n _ l o c k _ i n i t (&( d e v i c e −>b u f f e r [ i ] . l o c k ) ) ;
d e v i c e −>b u f f e r [ i ] . data = k m a l l o c ( 5 1 3 , GFP_KERNEL) ;
i f ( d e v i c e −>b u f f e r [ i ] . data == NULL) {
i f ( i != 0 ) {
k f r e e ( d e v i c e −>b u f f e r [ 0 ] . data ) ;
}
p r i n t k (KERN_ERR "DMX%i : k m a l l o c f a i l e d ! \ n" , pdev−>i d ) ;
goto m a l l o c _ e r r o r ;
} else {
memset ( d e v i c e −>b u f f e r [ i ] . data , 0 , 5 1 3 ) ;
107
895
897
899
901
903
905
907
909
911
913
915
917
919
921
923
925
927
929
931
933
935
937
939
941
943
945
947
}
}
/∗ R e s e t t h e u a r t ∗/
UART_PUT_CR( d e v i c e , AT91_US_RSTSTA | AT91_US_RSTRX | AT91_US_RSTTX) ;
/∗ S e t u p UART r e g i s t e r s ∗/
UART_PUT_MR( d e v i c e , DMX_MR_VAL) ;
d e v i c e −>d i v i d e r = ( d e v i c e −>u a r t c l k + ( 8 ∗ baud ) ) / ( 1 6 ∗ baud ) ;
UART_PUT_BRGR( d e v i c e , d e v i c e −>d i v i d e r ) ;
/∗ s e t t i m e o u t t o 1/4 o f a s ec o n d ∗/
UART_PUT_RTOR( d e v i c e , 0 x 0 0 0 0 f 4 2 4 ) ;
/∗ D i s a b l e UART I n t e r r u p t s ∗/
UART_PUT_IDR( d e v i c e , −1) ;
/∗ A l l o c a t e t h e i r q ∗/
r e t v a l = r e q u e s t _ i r q ( d e v i c e −>i r q , dmx_interrupt ,
//SA_SHIRQ, "at91_dmx " , d e v i c e ) ;
SA_INTERRUPT, "at91_dmx" , d e v i c e ) ;
if ( retval ) {
p r i n t k (KERN_ERR "DMX%i : IRQ r e q u e s t f a i l e d ! \ n" , pdev−>i d ) ;
goto i r q _ e r r o r ;
}
/∗ i n i t i a l i z e w a i t queue ’ s ∗/
i nit _w ai tq ueu e_ he ad (& d e v i c e −>i b u f q ) ;
/∗ b l o c k q i s n o t used anymore ! ! ! ∗/
/∗ c l e a r s t a t i s t i c s ∗/
d e v i c e −>r x c = 0 ;
d e v i c e −>t x c = 0 ;
/∗ S e t p l a t f o r m d r i v e r d a t a ∗/
p l a t f o r m _ s e t _ d r v d a t a ( pdev , d e v i c e ) ;
/∗ e n a b l e t x ∗/
UART_PUT_CR( d e v i c e , AT91_US_TXEN|AT91_US_RXEN) ;
/∗ Set−up IDLE mode ∗/
d e v i c e −>s t a t e = &i d l e s ;
t o s t a t e ( d e v i c e , &i d l e s , 0 ) ;
p r i n t k (KERN_DEBUG "DMX%i : dmx_probe was s u c c e s s f u l ! \ n" , pdev−>i d ) ;
return 0 ;
/∗ t a k i n g c a r e o f e r r o r o u s c o n d i t i o n s ∗/
irq_error :
malloc_error :
map_error :
release_mem_region ( d e v i c e −>mapbase , s i z e ) ;
requestmem_error :
pin_error :
g p i o _ d e t a c h _ c l i e n t (& d e v i c e −>g p i o ) ;
clk_error :
c l k _ d i s a b l e ( d e v i c e −>c l k ) ;
clk_put ( d e v i c e −>c l k ) ;
return −ENODEV;
}
/∗ dmx_remove − P l a t f o r m d e v i c e f u n c t i o n ∗/
s t a t i c i n t __devexit dmx_remove ( struct p l a t f o r m _ d e v i c e ∗ pdev )
{
struct dmxdevice ∗ d e v i c e = platform_get_drvdata ( pdev ) ;
int s i z e ;
int r e t = 0 ;
949
951
953
955
957
959
961
963 }
g p i o _ d e t a c h _ c l i e n t (& d e v i c e −>g p i o ) ;
c l k _ d i s a b l e ( d e v i c e −>c l k ) ;
clk_put ( d e v i c e −>c l k ) ;
p l a t f o r m _ s e t _ d r v d a t a ( pdev , NULL) ;
i f ( device ) {
UART_PUT_IDR( d e v i c e , −1) ;
UART_PUT_CR( d e v i c e , AT91_US_TXDIS | AT91_US_RXDIS) ;
f r e e _ i r q ( d e v i c e −>i r q , d e v i c e ) ;
s i z e = pdev−>r e s o u r c e [ 0 ] . end − pdev−>r e s o u r c e [ 0 ] . s t a r t + 1 ;
release_mem_region ( d e v i c e −>mapbase , s i z e ) ;
iounmap ( d e v i c e −>membase ) ;
d e v i c e −>membase = NULL;
}
return r e t ;
108
Code Listings
/∗++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++∗/
965 /∗ Module e n t r y / e x i t p o i n t s ∗/
s t a t i c i n t __init at91_dmx_init ( void )
967 {
p r i n t k (KERN_DEBUG "DMX: R e g i s t e r i n g cdev %d\n" , DMX_MAJOR) ;
969
i f ( r e g i s t e r _ c h r d e v (DMX_MAJOR, "at91_dmx" , &dmxdev_fops ) ) {
p r i n t k ( KERN_ERR "DMX: r e g i s t e r major %d f a i l e d . \ n" , DMX_MAJOR) ;
971
return −EIO ;
}
973
/∗ r e g i s t e r p r o c e n t r y ∗/
proc_ entry = c r e a t e _ p r o c _ r e a d _ e n t r y ( "dmx" , 0 , NULL,
975
dmx_proc_read , NULL) ;
p r i n t k (KERN_INFO "DMX: R e g i s t e r i n g dmx p l a t f o r m d r i v e r . \ n" ) ;
977
return p l a t f o r m _ d r i v e r _ r e g i s t e r (&at91_dmx_driver ) ;
}
979
s t a t i c void __exit at91_dmx_exit ( void )
981 {
/∗ w i l l r e s u l t i n a dmx_remove ( ) c a l l s ∗/
983
p l a t f o r m _ d r i v e r _ u n r e g i s t e r (&at91_dmx_driver ) ;
/∗ u n r e g i s t e r / p r o c e n t r y ∗/
985
remove_proc_entry ( "dmx" , NULL /∗ p a r e n t d i r ∗/ ) ;
/∗ u n r e g i s t e r c h a r a c t e r d r i v e r ∗/
987
i f ( u n r e g i s t e r _ c h r d e v (DMX_MAJOR, "at91_dmx" ) )
p r i n t k ( KERN_ERR "DMX: u n r e g i s t e r major %d f a i l e d \n" , DMX_MAJOR) ;
989 }
991 m o d u l e _ i n i t ( at91_dmx_init ) ;
module_exit ( at91_dmx_exit ) ;
993 MODULE_AUTHOR( "Thomas Langewouters " ) ;
AT91RM9200 DMX header filer:
1 /∗ d r i v e r c o n s t a n t macro ’ s −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−∗/
/∗ Mode R e g i s t e r v a l u e s f o r DMX512 ∗/
3 #define DMX_MR_VAL AT91_US_USMODE_NORMAL | AT91_US_CHRL_8 | AT91_US_NBSTOP_2 |
AT91_US_PAR_NONE
5 /∗ number o f t i m e o u t s b e f o r e marking t h e c h a n n e l as i d l e ∗/
#define DMX_TO_PATIENCE 4
7
/∗ u s e ttyAT , major 204 and minor 154 −169. ∗/
9 #define DMX_MAJOR
204
#define MINOR_START
154
11 #define PROC_FILENAME "dmx"
13 /∗ macros f o r w r i t i n g t o t h e c o n t r o l l e r r e g i s t e r s −−−−−−−−−−−−−−−−−−−−−∗/
#define UART_PUT_CR( p o r t , v ) w r i t e l ( v , ( p o r t )−>membase + AT91_US_CR)
15 #define UART_GET_MR( p o r t ) r e a d l ( ( p o r t )−>membase + AT91_US_MR)
#define UART_PUT_MR( p o r t , v ) w r i t e l ( v , ( p o r t )−>membase + AT91_US_MR)
17 #define UART_PUT_IER( p o r t , v )
w r i t e l ( v , ( p o r t )−>membase + AT91_US_IER)
#define UART_PUT_IDR( p o r t , v )
w r i t e l ( v , ( p o r t )−>membase + AT91_US_IDR)
19 #define UART_GET_IMR( p o r t )
r e a d l ( ( p o r t )−>membase + AT91_US_IMR)
#define UART_GET_CSR( p o r t )
r e a d l ( ( p o r t )−>membase + AT91_US_CSR)
21 #define UART_GET_CHAR( p o r t ) r e a d l ( ( p o r t )−>membase + AT91_US_RHR)
#define UART_PUT_CHAR( p o r t , v ) w r i t e l ( v , ( p o r t )−>membase + AT91_US_THR)
23 #define UART_GET_BRGR( p o r t ) r e a d l ( ( p o r t )−>membase + AT91_US_BRGR)
#define UART_PUT_BRGR( p o r t , v ) w r i t e l ( v , ( p o r t )−>membase + AT91_US_BRGR)
25 #define UART_PUT_RTOR( p o r t , v ) w r i t e l ( v , ( p o r t )−>membase + AT91_US_RTOR)
/∗ PDC r e g i s t e r s −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−∗/
27 /∗ t r a n s f e r c o n t r o l & s t a t u s r e g i s t e r ∗/
#define UART_PUT_PTCR( p o r t , v ) w r i t e l ( v , ( p o r t )−>membase + AT91_PDC_PTCR)
29 #define UART_GET_PTSR( p o r t ) r e a d l ( ( p o r t )−>membase + AT91_PDC_PTSR)
31 /∗ r e c e i v e r p o i n t e r / c o u n t e r + n e x t p o i n t e r / c o u n t e r r e g i s t e r s ∗/
#define UART_PUT_RPR( p o r t , v )
w r i t e l ( v , ( p o r t )−>membase + AT91_PDC_RPR)
33 #define UART_PUT_RCR( p o r t , v )
w r i t e l ( v , ( p o r t )−>membase + AT91_PDC_RCR)
#define UART_PUT_RNPR( p o r t , v ) w r i t e l ( v , ( p o r t )−>membase + AT91_PDC_RNPR)
35 #define UART_PUT_RNCR( p o r t , v ) w r i t e l ( v , ( p o r t )−>membase + AT91_PDC_RNCR)
/∗ t h e s e two a l s o have t o be r e a d a f t e r r x t r a n s f e r : ∗/
109
37 #define UART_GET_RPR( p o r t )
r e a d l ( ( p o r t )−>membase + AT91_PDC_RPR)
#define UART_GET_RCR( p o r t )
r e a d l ( ( p o r t )−>membase + AT91_PDC_RCR)
39
/∗ t r a n s m i t p o i n t e r / c o u n t e r + n e x t p o i n t e r / c o u n t e r ∗/
41 #define UART_PUT_TPR( p o r t , v )
w r i t e l ( v , ( p o r t )−>membase + AT91_PDC_TPR)
#define UART_PUT_TCR( p o r t , v )
w r i t e l ( v , ( p o r t )−>membase + AT91_PDC_TCR)
43 #define UART_PUT_TNPR( p o r t , v ) w r i t e l ( v , ( p o r t )−>membase + AT91_PDC_TNPR)
#define UART_PUT_TNCR( p o r t , v ) w r i t e l ( v , ( p o r t )−>membase + AT91_PDC_TNCR)
45
struct dmxdevice ;
47 struct w s t a t e ;
49 struct dmxbuffer {
spinlock_t
lock ;
/∗ l o c k ∗/
51
char
∗ data ;
/∗ p o i n t e r t o a l l o c a t e d b u f f e r ∗/
dma_addr_t dma_addr ; /∗ dma a d d r e s s ∗/
53
int
len ;
/∗ l e n g t h o f frame ∗/
int
touched ;
/∗ o n l y u s e d a t a i f i t i s f r e s h ∗/
55 } ;
57 struct dmxdevice {
spinlock_t
lock ;
/∗ p o r t l o c k ∗/
59
unsigned char __iomem ∗membase ; /∗ u a r t r e g i s t e r s b a s e a d r e s s ∗/
unsigned long
mapbase ;
/∗ f o r ioremap ∗/
61
unsigned i n t
irq ;
/∗ i r q number ∗/
unsigned i n t
uartclk ;
/∗ b a s e u a r t c l o c k ∗/
63
unsigned i n t
divider ;
/∗ mck c l o c k d i v i d e r ∗/
at91_gpio_client
gpio ;
/∗ g p i o t h i n g ∗/
65
struct c l k
∗ clk ;
/∗ c l o c k s i g n a l ∗/
struct d e v i c e
∗ dev ;
/∗ p l a t f o r m d e v i c e ∗/
67
struct w s t a t e ∗ s t a t e ;
/∗ s t a t e o f t h e worker p r o c e s s ∗/
69
int p a t i e n c e ;
/∗ t i m e o u t c o u n t e r ∗/
71
int s i l e n c e ;
/∗ b o o l no i n p u t d a t a ! ∗/
int f e r r ;
/∗ nr . o f f r a m i n g e r r o r s ∗/
73
int o v e r f ;
/∗ number o f t i m e s u s e r i g n o r e d new d a t a ∗/
i n t rxc , t x c ;
/∗ t r a n s m i t t e d and r e c e i v e d DMX frames ∗/
75
struct dmxbuffer
buffer [ 2 ] ;
/∗ two w o r k b u f f e r s ∗/
77
struct dmxbuffer ∗ wbuf ;
/∗ p o i n t e r t o w o r k e r b u f f e r ∗/
struct dmxbuffer ∗ i b u f ;
/∗ p o i n t e r t o i n t e r m e d i a t e b u f f e r ∗/
79
wait_queue_head_t i b u f q ;
/∗ w a i t q u e u e t o s i g n a l i b u f a v a i l a b i l i t y ∗/
wait_queue_head_t b l o c k q ;
/∗ w a i t q u e u e t o awake b l o c k i n g r e a d s ∗/
81 } ;
83 struct w s t a t e {
unsigned
85
int
events ;
/∗ UART−i n t e r r u p t mask ∗/
char ∗ d e s c r i p t i o n ; /∗ s t a t e d e s c r i p t i o n ( used i n proc_read , p r i n t k ) ∗/
87
void ( ∗ s e t u p ) ( struct dmxdevice ∗ d e v i c e ,
struct w s t a t e ∗ o l d s t a t e ,
89
int i n t s r c
);
void ( ∗ s e r v i c e ) ( struct dmxdevice ∗ d e v i c e ,
91
struct w s t a t e ∗ r e q s t a t e ,
int i n t s r c
);
93 } ;
95 s t a t i c struct w s t a t e i d l e s , wlb , wsc , f b u f , t l b , tmab , t b u f ;
97 s t a t i c void i d l e s _ s e t u p
( struct dmxdevice ∗ d e v i c e
intsrc ) ;
s t a t i c void i d l e s _ s e r v i c e ( struct dmxdevice ∗ d e v i c e
);
99 s t a t i c void wlb_setup
( struct dmxdevice ∗ d e v i c e ,
;
s t a t i c void w l b _ s e r v i c e
( struct dmxdevice ∗ d e v i c e
);
101 s t a t i c void wsc_setup
( struct dmxdevice ∗ d e v i c e ,
;
, struct w s t a t e ∗ p r e v s t a t e , i n t
, struct w s t a t e ∗ r e q s t a t e , i n t i n t s r c
struct w s t a t e ∗ p r e v s t a t e , i n t i n t s r c )
, struct w s t a t e ∗ r e q s t a t e , i n t i n t s r c
struct w s t a t e ∗ p r e v s t a t e , i n t i n t s r c )
110
103
105
107
109
Code Listings
s t a t i c void w s c _ s e r v i c e
( struct dmxdevice ∗ d e v i c e
);
s t a t i c void f b u f _ s e t u p
( struct dmxdevice ∗ d e v i c e
intsrc ) ;
s t a t i c void f b u f _ s e r v i c e
( struct dmxdevice ∗ d e v i c e
);
s t a t i c void t l b _ s e t u p
( struct dmxdevice ∗ d e v i c e ,
;
s t a t i c void t l b _ s e r v i c e
( struct dmxdevice ∗ d e v i c e
);
s t a t i c void tmab_setup
( struct dmxdevice ∗ d e v i c e
intsrc ) ;
s t a t i c void t m ab _ s e rv i c e ( struct dmxdevice ∗ d e v i c e
);
s t a t i c void t b u f _ s e t u p
( struct dmxdevice ∗ d e v i c e
intsrc ) ;
s t a t i c void t b u f _ s e r v i c e
( struct dmxdevice ∗ d e v i c e
);
, struct w s t a t e ∗ r e q s t a t e , i n t i n t s r c
, struct w s t a t e ∗ p r e v s t a t e , i n t
, struct w s t a t e ∗ r e q s t a t e , i n t i n t s r c
struct w s t a t e ∗ p r e v s t a t e , i n t i n t s r c )
, struct w s t a t e ∗ r e q s t a t e , i n t i n t s r c
, struct w s t a t e ∗ p r e v s t a t e , i n t
, struct w s t a t e ∗ r e q s t a t e , i n t i n t s r c
, struct w s t a t e ∗ p r e v s t a t e , i n t
, struct w s t a t e ∗ r e q s t a t e , i n t i n t s r c
111
s t a t i c i n l i n e i n t i d e n t d e v ( struct dmxdevice ∗ d e v i c e ) ;
113 void i d e n t i n t s ( struct dmxdevice ∗ d e v i c e , unsigned i n t i n t s r c ) ;
s t a t i c void t o s t a t e ( struct dmxdevice ∗ d e v i c e , struct w s t a t e ∗ n e x t s t a t e , unsigned i n t
intsrc ) ;
115
s t a t i c i r q r e t u r n _ t dmx_interrupt ( i n t i r q , void ∗ dev_id , struct p t_ reg s ∗ r e g s ) ;
117 s t a t i c i n t dmx_cdev_release ( struct i n o d e ∗ i n o d e , struct f i l e ∗ f i l e ) ;
s t a t i c i n t dmx_cdev_open ( struct i n o d e ∗ i n o d e , struct f i l e ∗ f i l e ) ;
119 s t a t i c s s i z e _ t dmx_cdev_write ( struct f i l e ∗ f i l e , const char __user ∗ b u f f e r , s i z e _ t count ,
l o f f _ t ∗ ppos ) ;
s t a t i c s s i z e _ t dmx_cdev_read ( struct f i l e ∗ f i l e , char __user ∗ b u f f e r , s i z e _ t count , l o f f _ t
∗ ppos ) ;
121 s t a t i c i n t dmx_cdev_ioctl ( struct i n o d e ∗ i n o d e , struct f i l e ∗ f i l e , unsigned i n t cmd ,
unsigned long a r g ) ;
s t a t i c unsigned i n t dmx_cdev_poll ( struct f i l e ∗ f i l e , struct p o l l _ t a b l e _ s t r u c t ∗ t a b l e ) ;
123 s t a t i c i n t dmx_proc_read ( char ∗ buf , char ∗∗ s t a r t , o f f _ t o f f s e t , i n t l e n , i n t ∗ unused_i ,
void ∗ unused_v ) ;
s t a t i c i n t __devinit dmx_probe ( struct p l a t f o r m _ d e v i c e ∗ pdev ) ;
125 s t a t i c i n t __devexit dmx_remove ( struct p l a t f o r m _ d e v i c e ∗ pdev ) ;
s t a t i c i n t __init at91_dmx_init ( void ) ;
127 s t a t i c void __exit at91_dmx_exit ( void ) ;
Art-Net stub program:
1 #include
#include
3 #include
#include
5 #include
#include
7 #include
#include
9 #include
#include
11 #include
#include
13 #include
15 #define
#define
17 #define
#define
19 #define
#define
21 #define
<s t d i o . h>
< s t d l i b . h>
< s t r i n g . h>
<u n i s t d . h>
< f c n t l . h>
<t e r m i o s . h>
<s y s / w a i t . h>
<s y s / time . h>
<s y s / s t a t . h>
<time . h>
<e r r n o . h>
" a r t n e t / a r t n e t . h"
" a r t n e t / p a c k e t s . h"
DMX_CHANNELS 512
DEFAULT_DEVICE " / dev /dmx0"
DEFAULT_DEVICE2 " / dev /dmx1"
DEFAULT_DEVICE3 " / dev /dmx2"
DEFAULT_DEVICE4 " / dev /dmx3"
ARTNET_MAX_DMX 4
UNCONNECTED −1
23 struct n o d e c o n f i g _ t {
i n t num_ports ;
25
char a r t s u b ;
char ipmode ;
27
char i p a d d r [ 1 7 ] ;
111
29
char i p s u b [ 1 7 ] ;
char short_name [ 1 6 ] ;
char long_name [ 6 4 ] ;
31 } ;
33 typedef struct {
int verbose ;
35
char ∗ dev [ARTNET_MAX_PORTS] ;
char ∗ ip_addr ;
37
i n t subnet_addr ;
i n t port_addr ;
39
int p e r s i s t ;
i n t num_ports ;
41
i n t f d [ARTNET_MAX_PORTS] ;
u i n t 8 _ t dmx [ARTNET_MAX_PORTS] [ARTNET_MAX_DMX +1] ;
43
char ∗ short_name ;
char ∗ long_name ;
45
char ∗ c o n f i g _ f i l e ;
} opts_t ;
47
i n t dmx_handler ( artnet_node n , i n t p o r t , void ∗d )
49 {
u i n t 8 _ t ∗ data ;
51
opts_t ∗ ops = ( opts_t ∗ ) d ;
int len , i ;
53
p r i n t f ( "De maarten i s ne p o s e r \n" ) ;
p r i n t f ( "De p o o r t i s %d\n" , p o r t ) ;
55
data=artnet_read_dmx ( n , p o r t , &l e n ) ;
p r i n t f ( "De ontvangen data v o l g t : \ n" ) ;
57
f o r ( i =1; i <( l e n / 8 ) ; i ++)
{
59
p r i n t f ( "C %3d : %3d\ t " , i , data [ i −1]) ;
i f ( i %8==0) p r i n t f ( " \n" ) ;
61
}
p r i n t f ( " \n" ) ;
63
return 0 ;
}
65
i n t program_handler ( artnet_node n , void ∗d )
67 {
return 0 ;
69 }
71 i n t main ( i n t a r g c , char a r g v [ ] )
{
73
int i ;
artnet_node node ;
75
opts_t ∗ n o d e _ c o n f i g ;
node_config−>v e r b o s e = 0 ;
77
node_config−>ip_addr = NULL;
node_config−>subnet_addr = 0 ;
79
node_config−>port_addr = 0 ;
node_config−>p e r s i s t = 0 ;
81
node_config−>num_ports = 1 ;
f o r ( i =0; i < ARTNET_MAX_PORTS; i ++) {
83
node_config−>f d [ i ] = UNCONNECTED;
memset ( node_config−>dmx [ i ] , 0 x00 , ARTNET_MAX_DMX+1) ;
85
}
node_config−>dev [ 0 ] = s t r d u p (DEFAULT_DEVICE) ;
87
node_config−>dev [ 1 ] = s t r d u p (DEFAULT_DEVICE2) ;
node_config−>dev [ 2 ] = s t r d u p (DEFAULT_DEVICE3) ;
89
node_config−>dev [ 3 ] = s t r d u p (DEFAULT_DEVICE4) ;
node_config−>short_name = s t r d u p ( "KBDesign atNode " ) ;
91
node_config−>long_name = s t r d u p ( "KB D e s i g n Art−Net Node" ) ;
node_config−>c o n f i g _ f i l e = NULL ;
93
node = artnet_new ( 0 , 1 ) ;
artnet_set_subnet_addr ( node , 0 ) ;
95
artnet_set_short_name ( node , node_config−>short_name ) ;
artnet_set_long_name ( node , node_config−>long_name ) ;
97
artnet_set_node_type ( node , ARTNET_NODE) ;
112
Code Listings
a r t n e t _ s e t _ p o r t _ t y p e ( node , 0 , ARTNET_ENABLE_OUTPUT, ARTNET_PORT_DMX) ;
artnet_set_port_addr ( node , 0 , ARTNET_OUTPUT_PORT, 1 ) ;
artnet_set_program_handler ( node , program_handler , ( void ∗ ) n o d e _ c o n f i g ) ;
artnet_set_dmx_handler ( node , dmx_handler , ( void ∗ ) n o d e _ c o n f i g ) ;
a r t n e t _ s t a r t ( node ) ;
while ( 1 )
{
sleep (3) ;
p r i n t f ( " I k ga nu de r e a d doen \n" ) ;
a r t n e t _ r e a d ( node , 0 ) ;
p r i n t f ( "En nu ben i k e r u i t \n" ) ;
printf (" . ") ;
f f l u s h (0) ;
}
return 1 ;
99
101
103
105
107
109
111
113 }
DMX output stub program:
1 #include
#include
3 #include
#include
5 #include
#include
7 #include
<s t d i o . h>
< s t d l i b . h>
<u n i s t d . h>
< p o l l . h>
<i n t t y p e s . h>
<s y s / s t a t . h>
< f c n t l . h>
9 i n t main ( i n t ar g c , char ∗∗ a r g v )
{
11
int i , len , t e s t p a t t e r n 1 , t e s t p a t t e r n 2 , fd ;
u i n t 8 _ t data1 [ 5 1 2 ] ;
13
u i n t 8 _ t data2 [ 5 1 2 ] ;
struct p o l l f d s e t ;
15
i f ( a r g c !=4)
{
17
p r i n t f ( " Usage : DMXWrite f i l e 1 . b i n f i l e 2 . b i n d e v i c e \n" ) ;
return 1 ;
19
}
t e s t p a t t e r n 1=open ( a r g v [ 1 ] , O_RDONLY) ;
21
t e s t p a t t e r n 2=open ( a r g v [ 2 ] , O_RDONLY) ;
f d = open ( a r g v [ 3 ] , O_RDWR | O_NOCTTY | O_NONBLOCK ) ;
23
if ( testpattern1 < 0 | | testpattern2 < 0 )
{
25
p r i n t f ( "One o f t h e f i l e s was not found \n" ) ;
return 1 ;
27
}
i f ( f d < 0 ) { p r i n t f ( " E r r o r o p e n i n g DMX p o r t \n" ) ; return 1 ; }
29
r e a d ( t e s t p a t t e r n 1 , data1 , 5 1 2 ) ;
r e a d ( t e s t p a t t e r n 2 , data2 , 5 1 2 ) ;
31
set . fd = fd ;
s e t . e v e n t s = POLLOUT;
33
while ( 1 )
35 {
p o l l (& s e t , 1 , −1) ;
37
l e n = w r i t e ( fd , data1 , 5 1 2 ) ;
f o r ( i =0; i <64 ; i ++)
39
{
p r i n t f ( "%3d : %3d\ t " , i +1 , data1 [ i ] ) ;
41
i f ( i %8 == 0 ) p r i n t f ( " \n" ) ;
43
45
}
p r i n t f ( " \n%d b y t e s w r i t t e n , now p a u s i n g . . . \ n" , l e n ) ;
sleep (4) ;
47
49
51
p o l l (& s e t , 1 , −1) ;
l e n = w r i t e ( fd , data2 , 5 1 2 ) ;
i f ( len < 0)
{
113
p r i n t f ( " E r r o r w h i l e w r i t i n g s e c o n d frame \n" ) ;
return 1 ;
53
}
f o r ( i =0; i <64 ; i ++)
{
p r i n t f ( "%3d : %3d\ t " , i +1 , data2 [ i ] ) ;
i f ( i %8 == 0 ) p r i n t f ( " \n" ) ;
55
57
59
}
sleep (4) ;
p r i n t f ( " \n%d b y t e s w r i t t e n , now p a u s i n g . . . \ n" , l e n ) ;
61
63 }
close ( testpattern1 ) ;
close ( testpattern2 ) ;
c l o s e ( fd ) ;
return 0 ;
65
67
}
DMX input stub program:
#include <s t d i o . h>
2 #include < s t d l i b . h>
#include <u n i s t d . h>
4 #include <i n t t y p e s . h>
#include <s y s / s t a t . h>
6 #include < p o l l . h>
#include < f c n t l . h>
8
i n t main ( void )
10 {
i n t i , l e n , fd , f r a m e s ;
12
u i n t 8 _ t data [ 5 1 2 ] ;
struct p o l l f d wset ;
14
f d = open ( " / dev /dmx1" , O_RDONLY | O_NOCTTY | O_NONBLOCK) ;
i f ( f d < 0 ) { p r i n t f ( " E r r o r o p e n i n g DMX p o r t \n" ) ; return 1 ; }
16
f r a m e s =0;
wset . f d = f d ;
18
wset . e v e n t s = POLLIN ;
while ( 1 )
20
{
p o l l (&wset , 1 , −1) ;
22
l e n = r e a d ( fd , data , 5 1 2 ) ;
f o r ( i =0;( l e n != 0 ) && i <512 && i <l e n ; i ++)
24
{
i f ( data [ i ] != 0 )
26
{
p r i n t f ( "%3d : %3d\ t " , i +1 , data [ i ] ) ;
28
i f ( i %8 == 0 ) p r i n t f ( " \n" ) ;
}
30
}
f r a m e s ++;
32
p r i n t f ( " \ nCounted %d frames , l a s t frame was %d b y t e s \n" , frames , l e n ) ;
}
34
return 0 ;
}