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§2=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 ; }