Linux Device Drivers
Gatliff, Bill
"Supported by Linux" rarely means "it will work for you". In fact, most vendor-supplied device drivers require extensive modification-and often a complete rewrite-before deployment in embedded environments.This presentation offers a list of best practices, with examples, drawn from the author's decade of experience writing real-world, high-complexity device drivers for Linux kernels. You'll learn how to make the compiler write bulletproof register access code for you, how to plan for hardware integration and field failures, and considerations for proactive, reactive, and interactive debugging scenarios. You'll also learn the key differences between "device drivers" and "interfaces", and how that knowledge translates into code that does more AND works better than the code you're delivering now.
 Good morning. My name is Bill Gatliff and I guess I'll be in a moment of honesty. I'll be terrified. I'm terrified to meet you, but I am grateful for the chance to be here. Oh, that's that is a hot mic. Okay. Yeah, so if I get too loud, let me know. First time in Oslo, first time at NDC, I've been involved with embedded systems conferences in America for more than 10 years. So this isn't the first time I've been on a stage. It's the first time here in Oslo. And like I said, I'm grateful for that opportunity. I am not a professional speaker. If you haven't figured that out already, I'm a developer just like most of you. And I spend a very small amount of my total time doing this. The rest of the time I'm locked away in a cave underneath my house writing code and so forth. So so what we're going to talk about in this presentation is all the time I've spent in that cave, making the same mistakes over and over again, I finally got wise one day and said, you know, maybe I should write some of this stuff down and try and come up with something that works better the next time around. And so when I'm using the term best practices here, what I'm really talking about is things that are best practices for the kind of work that I do for the kind of problems I solve for for my type of workflow. I'm a freelancer. I've been doing embedded Linux since about 1999. And most of the time I spend doing what I call high complexity device drivers, device drivers where it goes beyond just making an LED blink and reading a push button. One of my more recent clients made cell phones and we did some work on the accelerometers and touch screens and so forth for them. I've also done work with a company that does basically for you drill a hole like you're going to use to drill oil. And then this machine, which is I guess truck mounted, parks over that hole and drops a small explosive charge into the well. And then it measures the shock wave that comes back. And from that information, it can get inside into the condition of the well and a bunch of I don't I'm not a geologist. I heard the part that oh, there's going to be stuff that explodes. Okay, I'm in. And so for that one, I had to do an ADD converter that was running on a hardware time clock of one kilohertz. But we had to do direct memory transfer into user memory so that he could do a real time graph as the shock wave was echoing around inside the well. And so those kinds of things. So we were not only were we doing DMA, we were doing DMA directly into user space from the ADD converter itself with no intermediate hop inside kernel space like a traditional driver does. And we were able to get tons of throughput that way. And all of this stuff here that I'm about to show you is stuff that isn't just a good idea for me. It's the way I do my work now. And so when a client calls me and says we need a device driver, I usually say, well, let's talk about that part in a second, because we don't even know yet what your hardware looks like, whether your hardware actually works or anything, those more important considerations. Before we start talking about what do you want the interface to that hardware to look like? Because in the case of that ADD converter example, the interface for it was basically a memory pointer that said, here's where I want you to put the data. And this is the number of samples I want you to give me go. And so it didn't have a traditional device node with an open, closed, read and write type interface, even though in Linux everything is a file, so to speak. And so, but that was the interface that helped us solve our problem. And I usually tell people that your job as a device driver author is mostly to babysit the hardware and make the application writer's job as easy as possible. What I mean by that is on the hardware side, you have to maintain control of whatever this chip is to prevent it from either running away with the system or not, you know, stops doing what it's supposed to do. And on the application side, you don't ever want to reveal to the authors of those applications how the hardware works. You always want to model the information that comes out of the hardware because that gives you usually that additional degree of separation gives you the ability to do things like change the hardware later, test the hardware with maybe even like a test-driven development kind of thing. Just throw that out there since he's right there. The guy that wrote the book on it, at least for him better to work and those sorts of things. So, and these are all techniques that I found that are helpful in that regard. This is not my first time to this part of the world. I had a client who took me to Malmo in Sweden for three weeks to troubleshoot a cranky LTE modem on a prototype handset and you can probably figure out who they were just from that information alone. They gave me about three weeks to do the work and so basically by about Wednesday of the first week, the first thing I did was sort of on Monday afternoon when I showed up, we did sort of a stop work and said, let's just throw some of this stuff into the system so that we can get inside into what the hardware is doing. By Wednesday, we knew what the problem was, we knew what the solution was and so I spent the next two and a half weeks doing that and it was really nice. But what I'm going to show you may not work for you but hopefully it'll make it some ideas going and the idea is if you find something that's truly better than what I'm doing here, we should all be stealing it from, stealing those kinds of ideas from each other because the main reason I stand up here is not because it gives me sort of a quasi-free trip to this part of the world a couple of times a year but it's really more personal than that. I have this recurring nightmare where I wake up in the dream anyway, I wake up in the hospital after some horrific accident plugged into a machine that I designed and that nightmare is bad enough but there's one that's slightly worse than that and that's awakening being dependent for life support on a machine that I didn't design and so what I'm trying to do here is if we all steal the best ideas from each other then that crap floor if you will just gets higher and higher and we all benefit from that so some day immortality is on the line here, right? So if I can get this slide to go in the right direction. So the general thinking for the kind of work that I do anyways like this, client calls me because they either have new hardware or they have a problem and they want me to solve it and so my thinking is usually along the lines of not if it's going to break but when it breaks what are the things I want to have in place to troubleshoot the system at that point. This question of a client coming to me saying I want a device driver, I said well you know that's really a question for me to answer for you not I mean yeah you're gonna need some kind of device driver at the end of the day or you wouldn't be calling me but the nature of that at the initial point of contact is usually much less important than hey let's make sure the hardware actually works and it turns out that that hardware works the hooks that you need in place to help validate that the hardware is working correctly you can get them in fairly quickly and then talk about the device driver stuff in parallel with them actually checking out the hardware itself and so that kind of stuff of what I would like to have present in the system can be done very quickly usually just in a couple of days even for the most complicated stuff but I want to I want to take an approach here that is such that we don't all the all the code that we're writing here is code that is safe enough that we can leave it in the production system because what's deployed to me might not be deployed to the customer you know I get off my desk and it goes back into their cubicle and I'm not necessarily done with it at that point because two months from now I may be having to visit this hardware again to figure out this new problem that they've created or they transferred out to whoever their customer is in the same situation over and over again so whatever it is that if it works well for us during this initial development phase of the project it'd be nice if we could do it in such a way that we could leave those tools available to us through the entire products lifecycle but our implementations can't do anything to make the problem any worse than it already is and so what I'm about to show you here are some things that it'll look kind of strange to you in spots you think well he's kind of really over complicated a few things but it's motivated by the fact of there's certain things I want to have but I don't want to trade off overall system quality to get them and so the general themes then for that are that my clicker is not cooperating today okay is that you'll see in a few minutes since this is a Linux device drivers class and most Linux is written in C I'm going to use the compiler in a lot of cases to do a lot of work that you guys are probably doing on your own right now which is a problem because there's a direct correlation between the number of lines you have code you write and the amount of debugging you'll do later and so anytime you can get a line of code out of your system it's always a good thing now it doesn't mean we should make the system more and more stupid so that we can make it safer and more correct now what I'm saying is we need to stop writing some some lines of code we shouldn't be writing we should be letting the system write for us and I'll show what I mean in a few minutes and but that's the idea here because what I've found is if we if we hand off certain development processes to the tools that were designed to do them for us not only will they do it more correctly but we can now work at the speed of thought and by that I mean I don't have to get bogged down with wealth the problem is with a data structure interaction or definition or something I can stay focused on that without having to get side tracked by the well as the interrupt handler for spin locks working and things like that because we already know in other in other parts of the system that we've tested that and it works and we know that it's going to continue work because nothing's changed just because we're working over another data file is that making it how many of you guys actually write device drivers awesome how many of you just don't want to and you're here because there's no better class there's a couple of you all right the honest ones anyway okay so if you've ever written a device driver in Linux then eventually you come around to this idea that you never tell Linux how to do its job right when you need a locking mechanism you ask Linux for one a spin lock or mutex or whatever the correct mechanism is and the same way it goes for when you want to power a chip up or down you never tell Linux what regulator to turn on and off you tell Linux please turn on whatever regulator if any it is that's connected to my VN pin for example and you make Linux sort out the details of okay what regulator is that if I'm going to turn that node in the circuit on I'm gonna have to power this up and power this up and power this up you let Linux figure all that stuff out and and and through experience you probably learned that the reason you do that is because if Linux understands enough of your schematic that it can power up voltage regulators in the correct order then it probably understands enough about your schematic that it can do other things correctly as well like making sure that it doesn't turn the lights out in that part of the circuit until it's told the driver that's responsible for this part of the circuit that it's going to do that those sorts of things and so when you can tell Linux only what you need done and Linux does it correctly that's usually a good sign that Linux understands your schematic enough that it can help you do your job and so so that's a factor in some of in the code that you'll see here another thing that you'll see reflected here quite a bit is how many of you read Alejandro Rubini's book called Linux device drivers all three editions right it's a good book but it's got to be one of the worst titles in the history of books and the reason I say that is because if you flip through every page of that book you see there's almost no discussion about device drivers as Linux defines them because in Linux you're not doing a device driver unless you're working with a data structure that's called device underscore driver nothing else matters and in Linux device drivers are all about power management they don't know about semaphores they don't know about c devs they don't know about miss devices those are interface implementations or synchronization primitives or interrupt handling there's no correlation between interrupt handling and device drivers except when you write the code that hooks the two of them together they're completely isolated concepts to Linux and when you really embrace that and say okay I'm not going to do interface related work in my device driver code and I'm not going to do device driver work in my interface related code all of a sudden a lot of your code gets a lot simpler because you're not telling Linux how to do its job you're just telling Linux what needs to be done and Linux can always do that better than you can I'll come back to that more in just a few minutes but just to excuse me to just kind of get back to the material at hand here the strategies that I'm going that you'll see in this code here is first of all since I'm a hardware guy I'm a device driver guy which means I'm I kind of got one foot in both grave right in each grave I want to be able to see what the hardware is doing from the programming environment and I also want to write code that I can move to another platform without having to rewrite a single line of in fact in some cases not even having to recompile it and that's actually possible even though a lot of the code that I see today won't do that and I'll show you some examples I'm not going to have my device driver working as an interface I'm not going to have my interface code working as a device driver and by that I mean it's very tempting inside of the open implementation for your device node well that's when I'll power the chip up and so as soon as using application opens the interface that's when I'll know that it's time to do something well while it's true that you may it may be that in your system the the correct time to power the chip up is when the user application calls that open method that doesn't mean your implementation in the file operations structure for the open functionality includes powering the chip up now what you do instead is you tell Linux to please if the chip's not turned on take it take it through its resume sequence in the same way as if they had just powered the system up from scratch or the system is just awakening from a from a suspend state or something like that and because if you if you put that code only where it belongs then you you've you've just taken out the chunk of code that I'll have to come back and write later that is well you know in their application they weren't doing runtime power management so they're not powering this chip on and off 10 times a second for their own reasons in addition to interacting with that open interface so we got to get that code out of there and and over to the right place and Linux can do that kind of stuff all day long so but if to just focus on the hardware parts there's enough to do right there because we need to make sure that we don't that when the datasheet says don't touch a register value we don't touch it and that we when the datasheet says to ignore a register value or a bit field we ignore it at all times and we want to make sure that we do that correctly every single time we interact with the chip and so you can imagine that well what what bill is really after here is adding a lot of error handling to our code and the answer is yes but no because that code clearly has to exist somewhere but you don't have to write it and and there's an app that will write it for you and it's called your C compiler and your brain and I'll show you in just a minute so but once you've got that stuff going it's kind of nice to be able to just sort of log into the system and then from the command line at least keep an eye on what the hardware is doing by being able to interact with those control registers if it's an SPI or an I2C chip and even some platform devices have control registers that are memory map that have interesting values that you'd want to see from the command line so but not just not just those guys there's you know USB has a similar model PCI all these guys and there's plenty of chips out there especially chips for things like touch controllers and GPIO controllers and so forth that have dual interface support where it's depending on how you wire one of the pins on the package it'll talk over SPI or I2C but the logic the behavioral model if you will the chip is exactly the same in both cases and if the chip hasn't changed any if the only thing that's changed is the interface we're using to talk to the chip why would we write the code again but that tends to be the case well it's I wrote the I2C driver now I need the SPI version so what are you going to start over now it's the only thing that's changed is the media transport layer if you will and so that's the only code that you should have to add for the new chip and and so that's the way that'll be part of this presentation as well so but so if there any questions at this point anyone anyone out in the overflow room okay hearing no objections we'll go ahead and start from the bottom and that is the low level the low level stuff that the code that Linux gives you to interact with peripheral control registers no matter where they are in the system and if you've done device drivers for Linux you've seen all these before memory mapped registers registers that are attached to an I2C bus registers that are attached to an SPI bus and so on and so on and so on there are models for these everywhere and at the end of the day you have to do this code somewhere but you don't want to do it any more than you have to and so and most developers know this and so when they have a need to take the chip through a reset operation for example they don't just necessarily throw that code into the four or five places in their in their module where they need that reset functionality they'll put it all into one function and then just call that function and why do you think it is they do that I'm sorry it's easy to read the code and make sure you've done it correctly right and and and and yeah so we already know some of the stuff that I'm going to be talking about is if this is the only place in the system where we do a reset and we have a problem with a reset we know right where to go look for the problem in the code because this is the only places and I'm just taking that that idea to the next level there's a little bit of nuance you'll see in a couple minutes here was some of the examples here are inspired by a Bosch BMA 250 accelerometer which is kind of cool because it's got a accelerometer and a rate gyro built into the same chip and it also has a separate power control register that has the various bits in it it's shared between both functionalities and then it also has this kind of cool bus interface matching logic where it can act like an an I squared C adapter so you can hook another chip usually a compass on the back end of it and the way you model all that stuff to Linux is is part of the Bosch chip you model as an I2C driver for the accelerometer a different one for the gyro you hook the power management logic in as a regulator that appears to be attached to both of those and that pass-through bus they call it the ox bus just looks like another I squared C adapter to Linux and so it looks like you actually written four or five different device drivers because you're interacting with four or five different device structures but what you've actually done is given Linux complete insight into into how all the different pieces of that chip fit together so that when Linux the text that well this application over here wants the gyro but he doesn't need the accelerometer the logic to turn on just the gyro and leave the accelerometer in whatever state it's in is already there and in the case of later on when you see me having a function like this rate here there's a control register in this chip that has a bit field called rate where you set the sampling rate for the chip and I've kind of oversimplified it a little bit because it's a little more there's a little more than just a few bits associated with it but the idea here is that at some point we need to manipulate that bit field in this control register but that's not what this this function is really that's not the service that this function is providing to the rest of the driver this is just set the sample rate of the chip and and then in the same way that we hid the logic of what specifically it means to do a reset what specifically it means to adjust the sampling rate for the chip is hidden inside of this function here and that the distinction of that will be a little clearer in just a minute but just as a look ahead so but we don't want to write code like I just saw like you just saw over and over and over again because we need to do these same sorts of things everywhere and every time that is making sure we interact with each one of these control registers correctly and that we can verify that we've done it correctly by inspection and the way the way I approach that so this would be the first best practice recommendation most of the drivers you see they they use something like a define for they'll usually have a macro for each one of the control registers that has the base address of the of the register in the in the devices register file not really changing that idea what I am going to do though is put in an enumeration instead for reasons you'll see in a couple of minutes and I'm also going to adopt a very regular naming convention here which is the name is exactly the same as it appears in the datasheet to the greatest extent possible and so that'll that'll go on for a while but in addition to this is the part that tends to be new in addition to having the address of the register I'm also going to have a regular convention for expressing the bits in that register that I'm not allowed to touch and in this case for example in the LSB the x-axis register the BMA 250 has two 8-bit registers one for the upper 8 bits and one for the lower 8 bits and the lower 8 bits most of those bits are invalid so 3e if you picture that bit field in your head you'll see it's just the lower EF I don't know it's just a few number of bits so the only ones you're paying attention to there and and I will will do that for every single control register in the system and have a regular naming convention which in my case is the name of the register underscore underscore reserved I have a very regular naming convention like that I will sit down and do this before I do any other code in the system because what you're actually doing here is you're describing the register file and each one of those bit fields to the compiler and I'll show you why that's that's helpful in just a second so this chip ID has no reserve bits they're all valid this x-axis LSB has a few reserve bits that's the the mask there and whether the convention is positive or negative is up to you and then the control one register here has a rate field that's these that these bits right here a one and an eight so they're kind of right in the middle of the byte and so so I sit down and that that transcribes that that table that's usually in the data sheet for the chip into its equivalent c code expression and then at the end of the day this is the code that we want to end up writing but we don't want to write it ourselves but this is code that'll be very very familiar to you if you've written device drivers before this is a a method for interacting with what's called a device attribute which are things you can see from the command line in the sys file system and the logic in this implementation so this is a we're going to interact allow the user to read out the value in the x-axis LSB register and so we'll take a lock and hold a lock so that nothing else is manipulating the chip while we're using it and then we'll just read the x-axis LSB and format it for them pretty straightforward stuff my motivation in the long run is to have this functionality for every register in the chip which might be a hundred or a hundred and fifty registers but if I write this code myself I'm going to get it wrong somewhere and and what's worse is if I write this code and and do get it wrong it's going to be a real pain in the neck to go fix it in all 150 places where this code appears so this is really is more pseudo code to me as a functional functional description of what I want even though this code would compile as is so I'm not going to write this code by hand I'm going to use c macros for that and if you've ever used these macros in c you'd be pretty off of the mainstream as far as most c programmers don't know what the double hash preprocessor operator does so I'll just explain it here I'm going to create a macro called BMA register read and it'll take a single argument which is the name of the register and anywhere you see hash hash and then that argument that parameter name there it will replace with whatever the the word was that you put in there when you invoke this macro and and so this code is for doing the low-level register read we'll come back and add the locking and so forth in a minute but this is for the low-level register read when I use this macro it's it's going to print a function called BMA reg read and then whatever the the name is there which will be as follows it'll it'll call a low-level single function called register read and pass it the enumeration that's associated with that register name and then the return result it will mask with that reserved bit field and then it will return that and so now you can see why I had the enumeration and that's because I want to make sure that these things get these tokens get expanded in the correct order I want the preprocessor to expand this but not until it knows the meanings of these things here and so if you get the order wrong and you're using macros instead of enumerations it doesn't work and so by using the enumerations you make sure that the preprocessor has an actual definition for this that's correct when it gets ready to express all this thing here so really the only thing that's novel about this is that instead of writing and copy pasting 150 of these one for each control register I write the code once and then I make the compiler write it for me 150 times and the way I would do that is just have one of this literally in my c code right below this this pound-defined statement usually I'll have a line for each one of the control registers and every time I have a statement that looks like this this is actually creating a function called bma read register x axis lsb that is the function's name and so well okay for chip i see if I was a professional speaker I would have used chip id as the example but you get the idea when I want to interact when I want to read the chip id I don't I don't do an i2c sm bus read byte right in that location in the code I call this little helper function here that that the compiler wrote for me called bma reg read chip id and that function would be this code but I only wrote this code once you're following so far okay so this isn't this isn't generally too difficult this is the x-axis lsb example here and we could you know put these together and have a higher level well I really don't ever want to see the x and y axis axes apart from each other and so I could have a bma read x-axis but there's no register named x-axis that would be a different function these are just a low level register accessor type functions because at this point the problem we were we were focused on solving is making sure that anytime we interact with any control register in the system we are minding the reserved bits and this will guarantee that that's the case as long as we have the programmer discipline to always interact with the control registers through that helper function we don't have to worry about reserved bits anymore I worked with the Hitachi sh2 part a few years ago and it had a dma controller that I was using that had a couple of bits in it that were marked as reserved and they just said write them a zero and I was like well okay and but there were other bits in that same register that I needed to interact with and without realizing I did a read a modify and then a write to turn those bits on you know x or equals 27 or something right and it turns out that although these bits were always write zero according to the datasheet you always had to write to them when you would read from them sometimes they would not give you zeros and so you could by doing that read modify write inadvertently write non-zero values back to these reserved bits which just so happened to cause the chip to flip into a factory test mode and I lost two days of my life on a stupid read modify write problem and that had I had the discipline back then to do this then that would have not it would not have been possible for me to get around masking those bits off because it would have in this case this is a reader you'll see in the write function it would always force those bits back to zero before they went out on the wire and so I wouldn't have to worry about that anymore yeah so here's the writer function it's the very same concept takes the value that I want to put into that register rips off the reserved bit field to make sure they're always set to zeros before it writes it out and this low-level function here we haven't written yet we'll come back to that in a little bit but but that by having the discipline to use that function there'd be no way those reserved bits could ever get set to anything but zero unless you know lucky meteor strike or something like that in which case that's probably the least of your problems right but um there actually was a minor bug in your code so pop quiz anybody see it yeah it's actually the single underscores reserved from the compiler double underscores as well all right so triple underscore I don't care yeah but it's a double underscore with another underscore right yeah thank you yeah the name it's yeah the paste operation is yeah that's the that's the correct term for it which out so no that's not the bug actually yeah I don't I guess if compilers are actually going to eat the whole double underscore space for themselves and linux is pretty much screwed because lots of code in the linux kernel it uses that namespace and so so yeah well gcc appears to get it right enough yeah the actual problem in this case is a subtle one but it this sort of vigilance is such a part of my core that when I made the mistake with this slide I left it in here as a reminder to no one else but me see the value that's actually getting written out is this value v here which we've ripped off the reserve bits and all that part's correct but if we compile this thing in debug mode the value that we're that we're printing out to the logs is prior to this reserved masking and so the value we present in the debugging log might not be the one that actually goes out on the wire so I don't if you'd call that a bug or a feature I don't know but so really you need to take this line and move it up there so all right so that's just a small bug but in the bigger picture what we're trying to do here is go away from well that looked a lot less green on my laptop a little while ago is is go from a sort of a code soup which is what you have with most device drivers that I see and ultimately end up having to rewrite where you've got multiple paths to where you can actually get data out onto the wire to talk to the tip to just a few that we have to correct to check and although if you actually look at the object file we'll have one of these little functions for every single control register in the system so there'll be a hundred of them there's only one place the code was actually written and so that's the only place we have to check to make sure it's correct and so this is a lot easier to check for correctness than this I would think the dashed line here was just sort of a mnemonic for okay if I'm in these functions I have to have that spin lock held it's for a different a different subject matter but the same idea and you can see there's really a nasty bug if you would actually do a function diagram like this for a lot of device drivers you tend to see behavior like this where well it briefly dropped the lock it briefly for example unlocked the spin lock and then took it again is that because these operations here are really independent of each other or did it actually need to stay locked there and he implemented it incorrectly well because of we're gonna as you'll see in a couple minutes we're gonna move the locking functionality to a place where it's unavoidable then we can guarantee that our system always looks like that and so it's just one less thing we have to deal with so that's the the basic reads and writes are usually pretty simple and so it's not usually a question but if you want to take a shot at it okay and don't right yeah go ahead this question was why have a why not have a parameterized function why have a separate function for each register because what I have what I've found when I do it like that is that I end up with a lot more redundant code in here and so I suppose that it may be the case that I was solving a problem where those redundancies were unavoidable and so the macro I ended up writing here was larger than it really could have been but when when I finally decided that this is the way that I would do things yeah it eats up additional space a little bit more space for each the function call overhead but it turns out the macro itself that you end up writing the amount of code you write is about as small as you can make it and so in in my case the objective was to write as few lines of code as possible and get as much functionality from them as possible and that's how I ended up with this you see in a in a couple of minutes we can get some of the behavior of that parameterization without actually having to write it and that's what we're gonna we're gonna go to in just a second here but anyone else yeah the offer yeah that's yeah that's a fairly recent addition and it it it claims to do a lot of the same kind of stuff for you and in fact it'll do the caching and and things like that for you as well for registers that are supposed to be read only and write only because it's although it's it seems like it's if it's your kernel version the very latest kernel version it's it's going to be available sort of across the board and a lot of the kernel versions I work with the api is not there yet because it's fairly recent and that's recent for embedded stuff yeah three years but but in the same way sometimes I need control over this low-level stuff in a way that I don't get with that with that api and sometimes it's just heavier than I want so although you could incorporate use of that api into the ideas that I'm talking about here it's not really this presentation I was trying to stay focused on the low-level stuff so I think that's an interesting api if you're working with a really recent kernel but I haven't banged away on it enough to know if it solves the kind of problems I'm wanting to solve here or not but that's a good point all right so just interacting with the ordinary read and write registers and reserve bit fields is kind of a corner case the really interesting stuff starts to happen when you're manipulating the bit fields of the of the of these individual control registers because then you've got things like read modify writes and shifting and so forth that you can get wrong and this would be a pretty traditional example of what I'm talking about here when we're going to set that sampling rate of the chip they read out the control register fiddle with the bits that they need to and then write them back out again very traditional and this is one of those examples where the register mapping api might not be all that much help because it's not going to give you this tight level of low level interaction control over what's actually going back and forth on the wire to the same degree that you might have here but like I said I'd I probably don't know enough about that api to make a blanket statement like that but it won't stop me right so because I've got the microphone but anyway so this is very traditional code but one of the things that that troubles me greatly about this code is the magic numbers here which seems like if we were going to do that whole enumeration to describe the register file why do we still have to have a magic number here we must have missed something somewhere and yeah we'll fix that in just a second but the other thing we can fix is well convert it so that it's using the actual register accessors instead of banging away on the raw low level you know register function itself so we get code that when we're interacting with this control register it's unavoidable those reserve bits can't leak out into us and when we do the read modify write the write down here is always stripping the reserve bits off so they can't leak back in so just switching to using our register accessor or higher level functions here was a small improvement but it's not really the improvement I'm after because I want to get rid of this thing here because this just to me screams like if I'm going to come around and modify that in some way later I'm going to get something wrong and I'm kind of doing work that the compiler should be doing for me and at least in the Linux kernel there's a function for that what we were trying to do here is move move this bit field that was passed as a parameter into its final location in the control register right and Linux will do that for you if you ask it to and that's why I use the enumeration with that mask for the ray field so if you go back in and I should have had that slide if you go backwards here to the to the definition of that macro or that enumeration that was the describing the bits that are associated with that rate bit field in the control register since those bits are all clumped together we can look at that as kind of a human and figure out how many bits we need to shift the incoming value into right and well you know we can have there's a function for that in Linux it's a macro called FFS and in most cases the FFS macro is a single assembly language instruction and and actually in a statement like this the compiler can probably figure it out and so while we have something here that looks like actual code we got we didn't get rid of the need excuse me we didn't get rid of the need for the shift because somebody somewhere has to do that to interact with with that bit field but what we did do is move the documentation back up into the enumeration and where it's now authoritative again and so if there's something wrong with the rate setting we know the problem isn't here the problem is up in our expression of the register file our description of the register file so i'm getting i'm getting logic out of this place and and and trying to get it to a location where i can inspect it and and have confidence that it's correct in just that one location and so this works for for bit fields and where where the bits are crunched together tightly into one control register there are plenty of examples of cases where like for example in some of the avr's timer counters where well they needed three bits of scaling and two of the bits are in this register and one of them is in in that register over there i don't know how else to deal with that except to do the code manually something that looks like this but still by going through the register accessors you get the masking and so forth is correct so you just you don't have to think about it anymore so there's that any questions on that okay so we've cleaned up our bit fields a little bit i think my batteries must be dead but our ever vigilant bug detector is tripped again and i don't know if you guys saw it or not and that is that um well although i can't really do it my naming convention uh for functions that have leading double underscores in the driver code is that you have to have the lock that's associated with a chip held before you call into this function and so this function bma set rate since it doesn't have that leading underscore assumes that uh uh that the lock is not held when you call into this function but i'm not taking the lock in here anywhere and i've got this horrible not horrible necessary read modify write that if it were to get interrupted by another interaction with that control register bad things might happen right so the bug here is that there's no locking and we need locking because that read modify write operation so the the only answer well there's two answers i guess one is throw the double underscore on do it and then make it the caller's problem to take the lock before he calls in which makes sense in some cases um but in this case i'm going to just throw in uh my locking function here which um i can make this an inline so it's a spin lock irq save or whatever i want but the locking mechanism itself since the signature would be the same throughout my driver i don't have to check it to make sure that it's correct later and since the function name here is going to be very regular you you can see a pattern developing here where you might be able to make some of this into a macro template of some kind as well but that kind of gets off the the beaten path so so that's dealing with bit fields um there's another class of special case here and that is registers that you can only write to and you can't read from yeah there's a few of them out there um or sometimes there's a register that when you write to it it gives you a different behavior than when you read from it it's like when you write to it it's you're clearing the interrupt status when you read from it it's telling you the interrupt status but it doesn't actually clear it those sorts of things and you can use this approach that i'm talking about here to make those registers behave a little more regularly so that you don't have to pay attention to that sort of uh what would you call it cognitive dissonance between well if you know my description of this chip is changing whether i'm reading or writing it and um and also it turns out some of this stuff makes it easier when you start doing runtime power management makes it actually makes it easier to do the rest of the driver and when i talk about runtime power management here i'm talking about the kernels runtime p.m. api where it makes it really super brain dead easy to if i have an accelerometer that i'm reading every 100 millisecond and it only takes me a millisecond to power it up take a measurement power it back down why would i leave it powered up the other 99 milliseconds linux is perfectly happy to just power it up and down when i need it and and and actually i don't have to pay attention to that myself or that's auto suspend functionality linux will just turn it on when it detects that it needs to be turned on and then turn it back off when it detects nobody's using it and i don't have to worry about that code at all and this code can help make some of that stuff a little easier to work and so so this is in this example i've got this range register which is uh is it a one g a two g a 10 g or a bazillion g uh filter on the accelerometer and it's it's um it's right only meaning you can't read it um so once you store the value into the range register if you want to put that value back later like when you've power cycled the chip during this suspend and resume somebody somewhere has to keep track of this thing's value and well we really don't want to have to do it ourselves in the code because eventually someone will forget but since we got this perfectly obvious spot here to do it the place to do it would be in the function that interacts with the range register right so so this is the original register writing template if you will and the only thing i've added here is this switch statement that says okay if the register that he's writing to is the range register then save a copy of it away so that we can get it back out later and what's nifty about this is at least with a gnu compiler and it's pretty straightforward i would expect most compilers to do this when you actually print out copies of this function if you print out the where the name is the range register then you get this code up here plus only this statement the bma range equals v and all that other stuff gets optimized away um and if you uh like so for example in this case i have a second one that's for the reset register i'll come back to that in just a second that code's not there except when you express this macro for the reset uh register name and if you're talking about neither of those registers the optimizer takes this whole switch statement out so if you go in and look in the assembly language you're actually getting exactly the code that you would write yourself even though it looks like i'm going to have a switch statement in every single copy of this function the optimizer takes it out because that's we're switching on something that's constant so the optimizer can figure out where it's going to go and it just gets rid of the rest of the code oh yeah before i forget if you guys want to copy this presentation afterwards just send me an email and i'll i'll send it to you because uh i guess here they don't have a link for you to download it all or anything so so this one has saved me um quite a few times because you'll see in a couple of minutes how i handle the range function but it turns out in the datasheet there's this little footnote on the bottom of page 996 right after the index that says anytime you interact with the reset register you got to wait for four milliseconds and uh gosh it'd be awfully nice if i could guarantee that well i can anytime i write to the reset register there's a use sleep statement right here for four milliseconds and as long as i have the program or discipline to when i interact with the reset register to use this function it's unavoidable so i can't forget it so i can't race ahead of the chip by mistake just because i forgot to add that extra line in my code so it's just one less thing i need to think about so now that we've got this range parameter this this bma structure is the structure you guys are already using to keep track of your device driver data it's the thing you malloc and your probe function and and so on and so in the right function we just stashed a copy of it away here in our read function well we can't actually read from that because it was a write only register so we're just going to return the value that we cashed away as a return result and so uh then uh that way when we write the value that we get back is always the value that we wrote to it we don't have to pay attention to uh saving it away because the function that i just showed you saved it for us right so but you can probably guess yeah so some of you are actually still awake yeah i've got kind of got i said that the range register was was uh was write only but here we did the read before the switch i got the the order reversed right so i like to throw those little pop quizzes in there because it's very gratifying when someone goes yeah i saw that yeah okay yeah there's no lock in this case but in this case the locking the lack of locking is okay because i am writing all these very these are like the last mile functions and i'm assuming the lock is already held anytime you call into these functions and so um yeah and so the reason then in this case i used uh the u sleep here that depending on your platform that's probably a bug because if you take that if you call that function with a spin lock the system can't actually sleep it's got to actually sit there and spin and there's two there's two types of delays and the linux kernel ones that spin and ones that actually will do a a weight over like a context which and uh and i think that actually needs to be a u delay not a u sleep so that you get the one that just sits there and busy waits because if you were to take that with a with a spin lock held the system on a single core machine anyway might get very unhappy with you anyway so that's the so that gets us um making sure that we're interacting with that reset register correctly we're interacting with the range register correctly and so now in the code anytime we interact with this reset register it's unavoidable we'll take that four millisecond weight um and so in our suspend and resume functionality um while we can in fact manually write the the uh the cash value back out to the registers when we're you know setting the state of the chip back to how it was before um we could also in our suspend logic just to a register read save and a register write and the read method doesn't actually do anything for the range register because it's not readable but our code could have that very regular pattern to it regardless and so and then so i like to sort of combine these macros and and so for registers that are readable and writable i'll have a uh an enclosing one that says that okay so this reset register is readable and writable the range register is write only the x-axis lsp register is read only chip id is read only because if i do that if i do it like that then um although it wasn't necessary in this case because it handled a read correctly you just saw that on the previous slide if i were to actually do it like this then there's no bma reg read range anywhere in the system so i can catch that mistake at compile time and so um if i try and do a read from in some cases that's a better approach than having a different simulated behavior for the range register to have no just never read from it and we can we can check authoritatively that we're not reading from it because the code will actually compile and link um where if we take this approach here but if we if we were to make it readable and write and writable or readable quote and writable quote by simulating the read operation then we can make it look like that instead so that's that okay so that's the basics of the very lowest level accessors and and the motivation behind them was even even if we're going to have 150 of these registers we don't want to write 150 copies of the same code but we want to make sure all the code is not only correct but that we can all see it on a page and verify that it's correct and so that was a motivation for all that stuff we're going to take it to a slightly higher level now in just a second but are there any questions yeah yeah um this question was what if you have to read from more than one register and um how do you handle the locking associated with that to make sure that you don't get interrupted um well there's I guess I'll refine the question slightly in in Linux there's no way to make sure that you don't get interrupted from this line and this line but there's a way to make sure that the system can't run the code between these two concurrently right and that's so you could have other unrelated code running on that processor over there or an interrupt handler could interrupt you and so forth but really what you're talking about is um in in this in our case we were using this lock here to throttle access to the chip as a whole so anytime you're interacting with the control registers of the chip you take this lock and while you hold this lock if somewhere else in the system someone reads from a sysfs attribute that's trying to export one of these register values like I'm going to show you in a second they got to wait until you put the lock back so as long as you hold this lock you can read and write the registers as long as you want without fear that anyone else gets into the chip and interacts with you at the same time so that's where my naming convention becomes important is that um I try and remind myself that look anytime I'm manipulating with these control registers I'm going to assume that the lock is held um and if I don't see a call somewhere above it to the locking function I'll be very suspicious of that code but for the reasons you use site it doesn't make sense in most cases to put the locking inside of this function because that would close off your ability to interact with multiple registers without interruption does that answer your question? right yeah so exactly yeah yeah so I might yeah I might have one function that returns an x y and z data structure so it's got the whole vector yeah so I'd have a that function would take the lock read from x read from y read from z and usually in the data sheet it'll tell you if you read them in this order even the hardware is inside the accelerometer is locking the register values so they're all tightly coupled in time and and so you read all three of those and then unlock and uh yeah does that answer your question to your satisfaction? all right yeah I guess at that point I punt to it's a manual a manual operation but if if you if it is something in the case of x y reading x y's and z's you you probably would put that in a function so you'd have all that logic in one place in using the same sort of strategy that's here so all right so but so now that we now that we can be sure we're interacting with these control registers within the driver itself I have often very often found it really nice to be able to sort of snoop on the hardware while it's running to see what's going on in fact that cranky lte modem example I opened the presentation with was an example of that the only thing I added was this was this ability right here and it gave me the ability to say oh well the reason the chip isn't talking to us is because something put it to sleep and uh I was able to do that by looking at the states of some of its status registers and so forth and say oh the chip is asleep well it's only like four places in the driver that put the chip to sleep so we can go look at those and it gives you a lot of insight into what's going on so usually when a client comes to me and says I want a device driver I say yeah we'll get to that the very first thing I give them is this kind of functionality one of these sysfs attribute files for every control register in the chip because usually the chip is uh although it may be really complicated they usually don't have to use all the complicated parts to make sure that the hardware is actually working correctly because well you know if you don't get a chip id and a version result that you're expecting odds are you put the chip in rotated 90 degrees from the way it's supposed to be and you can answer those kinds of questions without writing a whole device driver so wouldn't it be nice if we could just barf up code that look like this uh really really easily and in fact I can do it in about five minutes because I can just copy paste the macros that you just saw and copy paste a couple of macros compile and I'm good to go and so if you've got all this stuff correct like we talked about earlier ultimately this is the code that you want to have written which is this is a sysfs attribute a show method so this is when a user reads from that interface this would be the code that gets involved and this code's going to look exactly the same for each one of those control registers it's readable and and the pattern is take the lock get the data release the lock format the data send it back a very regular pattern here but we don't want to copy paste 150 copies of this code we want the compiler to do that for us and um and in fact there's actually another thing that's wrong with this code as well that I just kind of skimmed over and that is we're assuming that the chip we're talking to is on i squared c and that might not be the case because the bma 250 can come in either flavor and we don't want to have a compiler a flag or something here that says oh read it from i squared c or read it from spi because the driver doesn't know the platform integrator the guy who put the chip on the board is the only one who knows the driver shouldn't have to care and so there's a media assumption baked in here that we want to get rid of as well and so i'm going to throw templates at this as well and usually at this point my clients would go really do we really want a hundred you know when you do an ls and that's just directory we have 150 values show up and I go well yeah probably don't want all 150 of them but the one that you tell me to leave out is going to be the one you want six months from now so just put them all in there it doesn't take take any space at all so so the macro that we had for our register reading logic we're just going to take that same that same sort of strategy here the first is start using the methods that we've already written so that that part of the system is that part of the implementation is correct and then just commit the rest of it to a template there looks like this this is the the right version instead of the read version but the idea is the same so when a user echoes a value and directs the output into that cysfs attribute we end up here we take the string and format it according to so it's a cool capability of this string to unsigned long function here where you tell it the radix so am i expecting it in in hexadecimal or decimal and i suppose you could modify the name of the function here in some way because you might want to be able to convey to the user oh it's going to be in hexadecimal if you go in this interface or decimal if you go in this one but i just usually just tell them it's always in hexadecimal because that's the way the hardware thinks and so um so i don't i don't pay much attention to that but so so we do the string conversion here and then uh wow we're this is joy of a new presentation um and then take the lock do the read or in this case the write and then release the lock on the next slide all very straightforward stuff and we're going to use the switch statement just like we did before which is if you're talking about the power of the reset register that code will be there if you're not talking about either of those the optimizer takes all that code out and look we can make it so that it's unavoidable when they write to the reset register we wait that four milliseconds but we don't actually need that here because it's baked into this function right here so we can get rid of that special case entirely and um the power register was was for a related example more related to runtime power management but so that's the idea and so the default case here is just an ordinary read and then we unlock and return so that's the same sort of pattern as before yeah i know i'm way out over on time and so okay so any questions on that all right so just to sort of skim ahead to some of the syntax well it's a good thing you can download this right um one of the things you can check is is the locking being done correctly i use this macro quite a bit which is uh the spin spin locks have a try lock api that says is you can test and see if it's locked already and if it is it returns an error code and so inside of these functions you could even have a debugging mode that checks did they actually take the lock correctly if not kick out an error message um using the the try lock functionality like that and then i just gather these things up uh and then into this uh in in this example it's readable and writable so it expresses the read and write low level register accessors prints out the attribute show and store methods and then creates the attribute structure associated with them and so this one line of c code that i write plus the 10 or 12 lines of those of those defines ends up becoming uh multiplied by 150 registers a whole bunch of code but we didn't write it and so and what's more most that code fits on one page so we can make sure that it's correct so then uh this is the part that i haven't figured out how to automate yet which is i still haven't figured out how to make the compiler print this stuff out for me but i'm not sure that i really want to but so you gather this stuff up into an attribute structure and you pass it to the sysfs create group function but one of the nice things about not having it automated is you can sort of selectively take out the parts that the customer doesn't know he's going to want yet right and um or you can so you can do it at compile time which is not my preference my preference is something a little more dynamic like in a module variable and uh right um so the media agnostic part i'll just skip ahead skim skim skim because the sysfs attributes there was nothing media specific in there it just takes an ordinary device structure which is at the core of an spi device i swear it's the device whatever so this this attribute doesn't care the reason it cares is because we put this code in here where it shouldn't be and that's where you get to the uh to the lowest level helper function that i've been skipping over until now my data structure for the for the bma controller has uh pointers for both an i2c client and an spi device and uh because in my probe function i did a kz alec the one that i'm not using is going to be set to null and so in my low level read function here i can check oh am i using i2c if yes just do it over i2c otherwise if it's spi do it otherwise i have no idea how i got here and so and then there's nothing in linux that says so this would be your i2c probe uh function there's the kz alec and we said of all this will be familiar to anyone who's written i2c driver and then i just hand that off to a sort of a generic probe function right and from then on that's uh that's the logic for for connecting the chip up it's all baked in that function there the probe function for i2c is literally just 10 lines of code and there's nothing in linux that says you can't have um so the probe function is going to do the usual stuff there's nothing in linux that says you can't have multiple drivers registered in the same kernel module linux doesn't care kernel modules are a thing all by themselves and so uh so in cases like this bosh bma 250 that supports i2c and spi i'd register both the drivers because the only difference the only delta then in the memory footprint is going to be a very small amount of code for the data structure the very short spi probe function and i2c function and um and then whatever's in the device tree is going to define which one actually gets invoked because it isn't until the device tree gets parsed that linux calls one or the other of the probe functions and the logic for the as long as the logic for the chip is the same in both cases the only code that's different is i've got this stub function for i2c which does nothing other than initialize the data structure and then pass it over to a generic sort of mea agnostic implementation and then likewise for spi and so then at that point you've got a driver that you don't even have to recompile when you switch to the spi from the i2c version you have a driver that in a couple of pages of code you can guarantee that you're interacting with all 150 of those control registers correctly all the time every time and that you have something that you can interact with from the command line to keep an eye on the hardware and actually drive the hardware in some cases and you didn't have to write 150 times the amount of code that uh with 150 times the amount of bugs in it so unless you get paid to debug it's not really my thing but it's like that dillbert cartoon all right so any questions then i guess we probably ought to take them outside if there are any i'll be around uh i like email my emails is on the slides too so if you want to copy the presentation i'm happy to send it to you i'll answer questions and stuff as well so thank you very much and sorry i ran over
 Good morning. My name is Bill Gatliff and I guess I'll be in a moment of honesty. I'll  be terrified. I'm terrified to meet you, but I am grateful for the chance to be here. Oh,  that's that is a hot mic. Okay. Yeah, so if I get too loud, let me know. First time in  Oslo, first time at NDC, I've been involved with embedded systems conferences in America  for more than 10 years. So this isn't the first time I've been on a stage. It's the first time  here in Oslo. And like I said, I'm grateful for that opportunity. I am not a professional speaker.  If you haven't figured that out already, I'm a developer just like most of you. And I spend a  very small amount of my total time doing this. The rest of the time I'm locked away in a cave  underneath my house writing code and so forth. So so what we're going to talk about in this  presentation is all the time I've spent in that cave, making the same mistakes over and over again,  I finally got wise one day and said, you know, maybe I should write some of this stuff down and  try and come up with something that works better the next time around. And so when I'm using the  term best practices here, what I'm really talking about is things that are best practices for the  kind of work that I do for the kind of problems I solve for for my type of workflow. I'm a freelancer.  I've been doing embedded Linux since about 1999. And most of the time I spend doing what I call  high complexity device drivers, device drivers where it goes beyond just making an LED blink and  reading a push button. One of my more recent clients made cell phones and we did some work on  the accelerometers and touch screens and so forth for them. I've also done work with a company  that does basically for you drill a hole like you're going to use to drill oil. And then this  machine, which is I guess truck mounted, parks over that hole and drops a small explosive charge  into the well. And then it measures the shock wave that comes back. And from that information,  it can get inside into the condition of the well and a bunch of I don't I'm not a geologist. I heard  the part that oh, there's going to be stuff that explodes. Okay, I'm in. And so for that one,  I had to do an ADD converter that was running on a hardware time clock of one kilohertz. But we  had to do direct memory transfer into user memory so that he could do a real time graph as the shock  wave was echoing around inside the well. And so those kinds of things. So we were not only were  we doing DMA, we were doing DMA directly into user space from the ADD converter itself with no  intermediate hop inside kernel space like a traditional driver does. And we were able to get  tons of throughput that way. And all of this stuff here that I'm about to show you is stuff that  isn't just a good idea for me. It's the way I do my work now. And so when a client calls me and  says we need a device driver, I usually say, well, let's talk about that part in a second,  because we don't even know yet what your hardware looks like, whether your hardware actually works  or anything, those more important considerations. Before we start talking about what do you want  the interface to that hardware to look like? Because in the case of that ADD converter example,  the interface for it was basically a memory pointer that said, here's where I want you to put the  data. And this is the number of samples I want you to give me go. And so it didn't have a traditional  device node with an open, closed, read and write type interface, even though in Linux everything  is a file, so to speak. And so, but that was the interface that helped us solve our problem. And  I usually tell people that your job as a device driver author is mostly to babysit the hardware  and make the application writer's job as easy as possible. What I mean by that is on the hardware  side, you have to maintain control of whatever this chip is to prevent it from either running away  with the system or not, you know, stops doing what it's supposed to do. And on the application side,  you don't ever want to reveal to the authors of those applications how the hardware works. You  always want to model the information that comes out of the hardware because that gives you usually  that additional degree of separation gives you the ability to do things like change the hardware  later, test the hardware with maybe even like a test-driven development kind of thing. Just throw  that out there since he's right there. The guy that wrote the book on it, at least for him better  to work and those sorts of things. So, and these are all techniques that I found that are helpful  in that regard. This is not my first time to this part of the world. I had a client who took me  to Malmo in Sweden for three weeks to troubleshoot a cranky LTE modem on a prototype handset and  you can probably figure out who they were just from that information alone. They gave me about  three weeks to do the work and so basically by about Wednesday of the first week, the first thing  I did was sort of on Monday afternoon when I showed up, we did sort of a stop work and said,  let's just throw some of this stuff into the system so that we can get inside into what the  hardware is doing. By Wednesday, we knew what the problem was, we knew what the solution was and so  I spent the next two and a half weeks doing that and it was really nice. But what I'm going to show  you may not work for you but hopefully it'll make it some ideas going and the idea is if you find  something that's truly better than what I'm doing here, we should all be stealing it from, stealing  those kinds of ideas from each other because the main reason I stand up here is not because it gives  me sort of a quasi-free trip to this part of the world a couple of times a year but it's really  more personal than that. I have this recurring nightmare where I wake up in the dream anyway,  I wake up in the hospital after some horrific accident plugged into a machine that I designed  and that nightmare is bad enough but there's one that's slightly worse than that and that's  awakening being dependent for life support on a machine that I didn't design and so what I'm  trying to do here is if we all steal the best ideas from each other then that crap floor if you  will just gets higher and higher and we all benefit from that so some day immortality is on the  line here, right? So if I can get this slide to go in the right direction. So the general thinking  for the kind of work that I do anyways like this, client calls me because they either have new  hardware or they have a problem and they want me to solve it and so my thinking is usually along  the lines of not if it's going to break but when it breaks what are the things I want to have in  place to troubleshoot the system at that point. This question of a client coming to me saying I  want a device driver, I said well you know that's really a question for me to answer for you not I  mean yeah you're gonna need some kind of device driver at the end of the day or you wouldn't be  calling me but the nature of that at the initial point of contact is usually much less important  than hey let's make sure the hardware actually works and it turns out that that hardware works  the hooks that you need in place to help validate that the hardware is working correctly you can  get them in fairly quickly and then talk about the device driver stuff in parallel with them  actually checking out the hardware itself and so that kind of stuff of what I would like to have  present in the system can be done very quickly usually just in a couple of days even for the  most complicated stuff but I want to I want to take an approach here that is such that we don't  all the all the code that we're writing here is code that is safe enough that we can leave it in  the production system because what's deployed to me might not be deployed to the customer you know  I get off my desk and it goes back into their cubicle and I'm not necessarily done with it at  that point because two months from now I may be having to visit this hardware again to figure out  this new problem that they've created or they transferred out to whoever their customer is in  the same situation over and over again so whatever it is that if it works well for us during this  initial development phase of the project it'd be nice if we could do it in such a way that we  could leave those tools available to us through the entire products lifecycle but our implementations  can't do anything to make the problem any worse than it already is and so what I'm about to show  you here are some things that it'll look kind of strange to you in spots you think well he's kind  of really over complicated a few things but it's motivated by the fact of there's certain things I  want to have but I don't want to trade off overall system quality to get them and so the general  themes then for that are that my clicker is not cooperating today okay is that you'll see in a few  minutes since this is a Linux device drivers class and most Linux is written in C I'm going to use  the compiler in a lot of cases to do a lot of work that you guys are probably doing on your own  right now which is a problem because there's a direct correlation between the number of lines  you have code you write and the amount of debugging you'll do later and so anytime you can get a line  of code out of your system it's always a good thing now it doesn't mean we should make the system  more and more stupid so that we can make it safer and more correct now what I'm saying is we need to  stop writing some some lines of code we shouldn't be writing we should be letting the system write  for us and I'll show what I mean in a few minutes and but that's the idea here because what I've  found is if we if we hand off certain development processes to the tools that were designed to do  them for us not only will they do it more correctly but we can now work at the speed of thought and  by that I mean I don't have to get bogged down with wealth the problem is with a data structure  interaction or definition or something I can stay focused on that without having to get side tracked  by the well as the interrupt handler for spin locks working and things like that because we already  know in other in other parts of the system that we've tested that and it works and we know that  it's going to continue work because nothing's changed just because we're working over another  data file is that making it how many of you guys actually write device drivers awesome how many of  you just don't want to and you're here because there's no better class there's a couple of you  all right the honest ones anyway okay so if you've ever written a device driver in Linux then  eventually you come around to this idea that you never tell Linux how to do its job right when you  need a locking mechanism you ask Linux for one a spin lock or mutex or whatever the correct  mechanism is and the same way it goes for when you want to power a chip up or down you never  tell Linux what regulator to turn on and off you tell Linux please turn on whatever regulator if  any it is that's connected to my VN pin for example and you make Linux sort out the details of  okay what regulator is that if I'm going to turn that node in the circuit on I'm gonna have to  power this up and power this up and power this up you let Linux figure all that stuff out and and  and through experience you probably learned that the reason you do that is because if Linux understands  enough of your schematic that it can power up voltage regulators in the correct order then it  probably understands enough about your schematic that it can do other things correctly as well like  making sure that it doesn't turn the lights out in that part of the circuit until it's told the  driver that's responsible for this part of the circuit that it's going to do that those sorts of  things and so when you can tell Linux only what you need done and Linux does it correctly that's  usually a good sign that Linux understands your schematic enough that it can help you do your job  and so so that's a factor in some of in the code that you'll see here another thing that you'll see  reflected here quite a bit is how many of you read Alejandro Rubini's book called Linux device  drivers all three editions right it's a good book but it's got to be one of the worst titles in the  history of books and the reason I say that is because if you flip through every page of that book  you see there's almost no discussion about device drivers as Linux defines them because in Linux  you're not doing a device driver unless you're working with a data structure that's called device  underscore driver nothing else matters and in Linux device drivers are all about power management  they don't know about semaphores they don't know about c devs they don't know about miss devices  those are interface implementations or synchronization primitives or interrupt handling there's no  correlation between interrupt handling and device drivers except when you write the code that hooks  the two of them together they're completely isolated concepts to Linux and when you really  embrace that and say okay I'm not going to do interface related work in my device driver code  and I'm not going to do device driver work in my interface related code all of a sudden a lot of  your code gets a lot simpler because you're not telling Linux how to do its job you're just telling  Linux what needs to be done and Linux can always do that better than you can I'll come back to that  more in just a few minutes but just to excuse me to just kind of get back to the material at hand  here the strategies that I'm going that you'll see in this code here is first of all since I'm a  hardware guy I'm a device driver guy which means I'm I kind of got one foot in both grave right  in each grave I want to be able to see what the hardware is doing from the programming environment  and I also want to write code that I can move to another platform without having to rewrite a  single line of in fact in some cases not even having to recompile it and that's actually possible  even though a lot of the code that I see today won't do that and I'll show you some examples I'm  not going to have my device driver working as an interface I'm not going to have my interface  code working as a device driver and by that I mean it's very tempting inside of the open  implementation for your device node well that's when I'll power the chip up and so as soon as  using application opens the interface that's when I'll know that it's time to do something well  while it's true that you may it may be that in your system the the correct time to power the  chip up is when the user application calls that open method that doesn't mean your implementation  in the file operations structure for the open functionality includes powering the chip up now  what you do instead is you tell Linux to please if the chip's not turned on take it take it through  its resume sequence in the same way as if they had just powered the system up from scratch or the  system is just awakening from a from a suspend state or something like that and because if you  if you put that code only where it belongs then you you've you've just taken out the chunk of code  that I'll have to come back and write later that is well you know in their application they weren't  doing runtime power management so they're not powering this chip on and off 10 times a second  for their own reasons in addition to interacting with that open interface so we got to get that  code out of there and and over to the right place and Linux can do that kind of stuff all day long  so but if to just focus on the hardware parts there's enough to do right there because we need  to make sure that we don't that when the datasheet says don't touch a register value we don't touch  it and that we when the datasheet says to ignore a register value or a bit field we ignore it at  all times and we want to make sure that we do that correctly every single time we interact with  the chip and so you can imagine that well what what bill is really after here is adding a lot of  error handling to our code and the answer is yes but no because that code clearly has to exist  somewhere but you don't have to write it and and there's an app that will write it for you and  it's called your C compiler and your brain and I'll show you in just a minute so but once you've  got that stuff going it's kind of nice to be able to just sort of log into the system and then from  the command line at least keep an eye on what the hardware is doing by being able to interact with  those control registers if it's an SPI or an I2C chip and even some platform devices have control  registers that are memory map that have interesting values that you'd want to see from the command  line so but not just not just those guys there's you know USB has a similar model PCI all these  guys and there's plenty of chips out there especially chips for things like touch controllers and  GPIO controllers and so forth that have dual interface support where it's depending on how  you wire one of the pins on the package it'll talk over SPI or I2C but the logic the behavioral  model if you will the chip is exactly the same in both cases and if the chip hasn't changed any  if the only thing that's changed is the interface we're using to talk to the chip why would we  write the code again but that tends to be the case well it's I wrote the I2C driver now I need  the SPI version so what are you going to start over now it's the only thing that's changed is the  media transport layer if you will and so that's the only code that you should have to add for the  new chip and and so that's the way that'll be part of this presentation as well so but so if  there any questions at this point anyone anyone out in the overflow room okay hearing no objections  we'll go ahead and start from the bottom and that is the low level the low level stuff that the  code that Linux gives you to interact with peripheral control registers no matter where  they are in the system and if you've done device drivers for Linux you've seen all these before  memory mapped registers registers that are attached to an I2C bus registers that are attached to an  SPI bus and so on and so on and so on there are models for these everywhere and at the end of the  day you have to do this code somewhere but you don't want to do it any more than you have to and  so and most developers know this and so when they have a need to take the chip through a reset  operation for example they don't just necessarily throw that code into the four or five places in  their in their module where they need that reset functionality they'll put it all into one function  and then just call that function and why do you think it is they do that I'm sorry it's easy to  read the code and make sure you've done it correctly right and and and and yeah so we already know  some of the stuff that I'm going to be talking about is if this is the only place in the system  where we do a reset and we have a problem with a reset we know right where to go look for the  problem in the code because this is the only places and I'm just taking that that idea to the next  level there's a little bit of nuance you'll see in a couple minutes here was some of the examples  here are inspired by a Bosch BMA 250 accelerometer which is kind of cool because it's got a  accelerometer and a rate gyro built into the same chip and it also has a separate power control  register that has the various bits in it it's shared between both functionalities and then  it also has this kind of cool bus interface matching logic where it can act like an an  I squared C adapter so you can hook another chip usually a compass on the back end of it and the  way you model all that stuff to Linux is is part of the Bosch chip you model as an I2C driver for  the accelerometer a different one for the gyro you hook the power management logic in as a  regulator that appears to be attached to both of those and that pass-through bus they call it the  ox bus just looks like another I squared C adapter to Linux and so it looks like you actually  written four or five different device drivers because you're interacting with four or five  different device structures but what you've actually done is given Linux complete insight  into into how all the different pieces of that chip fit together so that when Linux the text that  well this application over here wants the gyro but he doesn't need the accelerometer the logic to  turn on just the gyro and leave the accelerometer in whatever state it's in is already there and  in the case of later on when you see me having a function like this rate here there's a control  register in this chip that has a bit field called rate where you set the sampling rate for the chip  and I've kind of oversimplified it a little bit because it's a little more there's a little more  than just a few bits associated with it but the idea here is that at some point we need to manipulate  that bit field in this control register but that's not what this this function is really that's not  the service that this function is providing to the rest of the driver this is just set the  sample rate of the chip and and then in the same way that we hid the logic of what specifically it  means to do a reset what specifically it means to adjust the sampling rate for the chip is hidden  inside of this function here and that the distinction of that will be a little clearer in  just a minute but just as a look ahead so but we don't want to write code like I just saw like you  just saw over and over and over again because we need to do these same sorts of things everywhere  and every time that is making sure we interact with each one of these control registers correctly  and that we can verify that we've done it correctly by inspection and the way the way I approach that  so this would be the first best practice recommendation most of the drivers you see they they use  something like a define for they'll usually have a macro for each one of the control registers that  has the base address of the of the register in the in the devices register file not really  changing that idea what I am going to do though is put in an enumeration instead for reasons you'll  see in a couple of minutes and I'm also going to adopt a very regular naming convention here  which is the name is exactly the same as it appears in the datasheet to the greatest extent  possible and so that'll that'll go on for a while but in addition to this is the part that tends to  be new in addition to having the address of the register I'm also going to have a regular convention  for expressing the bits in that register that I'm not allowed to touch and in this case for example  in the LSB the x-axis register the BMA 250 has two 8-bit registers one for the upper 8 bits and one  for the lower 8 bits and the lower 8 bits most of those bits are invalid so 3e if you picture that  bit field in your head you'll see it's just the lower EF I don't know it's just a few number of  bits so the only ones you're paying attention to there and and I will will do that for every single  control register in the system and have a regular naming convention which in my case is the name of  the register underscore underscore reserved I have a very regular naming convention like that I will  sit down and do this before I do any other code in the system because what you're actually doing  here is you're describing the register file and each one of those bit fields to the compiler and  I'll show you why that's that's helpful in just a second so this chip ID has no reserve bits they're  all valid this x-axis LSB has a few reserve bits that's the the mask there and whether the convention  is positive or negative is up to you and then the control one register here has a rate field that's  these that these bits right here a one and an eight so they're kind of right in the middle of the  byte and so so I sit down and that that transcribes that that table that's usually in the data sheet  for the chip into its equivalent c code expression and then at the end of the day this is the code  that we want to end up writing but we don't want to write it ourselves but this is code that'll be  very very familiar to you if you've written device drivers before this is a a method for interacting  with what's called a device attribute which are things you can see from the command line in the  sys file system and the logic in this implementation so this is a we're going to interact allow the  user to read out the value in the x-axis LSB register and so we'll take a lock and hold a lock so  that nothing else is manipulating the chip while we're using it and then we'll just read the x-axis  LSB and format it for them pretty straightforward stuff my motivation in the long run is to have  this functionality for every register in the chip which might be a hundred or a hundred and fifty  registers but if I write this code myself I'm going to get it wrong somewhere and and what's  worse is if I write this code and and do get it wrong it's going to be a real pain in the neck to  go fix it in all 150 places where this code appears so this is really is more pseudo code to me as a  functional functional description of what I want even though this code would compile as is  so I'm not going to write this code by hand I'm going to use c macros for that and if you've ever  used these macros in c you'd be pretty off of the mainstream as far as most c programmers don't know  what the double hash preprocessor operator does so I'll just explain it here I'm going to create a  macro called BMA register read and it'll take a single argument which is the name of the register  and anywhere you see hash hash and then that argument that parameter name there it will  replace with whatever the the word was that you put in there when you invoke this macro and and so  this code is for doing the low-level register read we'll come back and add the locking and so  forth in a minute but this is for the low-level register read when I use this macro it's  it's going to print a function called BMA reg read and then whatever the the name is there  which will be as follows it'll it'll call a low-level single function called register read and pass it  the enumeration that's associated with that register name and then the return result it will mask  with that reserved bit field and then it will return that and so now you can see why I had the  enumeration and that's because I want to make sure that these things get these tokens get expanded  in the correct order I want the preprocessor to expand this but not until it knows the meanings  of these things here and so if you get the order wrong and you're using macros instead of enumerations  it doesn't work and so by using the enumerations you make sure that the preprocessor has an actual  definition for this that's correct when it gets ready to express all this thing here so really  the only thing that's novel about this is that instead of writing and copy pasting 150 of these  one for each control register I write the code once and then I make the compiler write it for me  150 times and the way I would do that is just have one of this literally in my c code right below  this this pound-defined statement usually I'll have a line for each one of the control registers and  every time I have a statement that looks like this this is actually creating a function called  bma read register x axis lsb that is the function's name and so well okay for chip i see if I was a  professional speaker I would have used chip id as the example but you get the idea when I want to  interact when I want to read the chip id I don't I don't do an i2c sm bus read byte right in that  location in the code I call this little helper function here that that the compiler wrote for me  called bma reg read chip id and that function would be this code but I only wrote this code once  you're following so far okay so this isn't this isn't generally too difficult this is the x-axis  lsb example here and we could you know put these together and have a higher level well I really  don't ever want to see the x and y axis axes apart from each other and so I could have a bma  read x-axis but there's no register named x-axis that would be a different function these are just  a low level register accessor type functions because at this point the problem we were we were focused  on solving is making sure that anytime we interact with any control register in the system we are  minding the reserved bits and this will guarantee that that's the case as long as we have the  programmer discipline to always interact with the control registers through that helper function we  don't have to worry about reserved bits anymore I worked with the Hitachi sh2 part a few years ago  and it had a dma controller that I was using that had a couple of bits in it that were marked as  reserved and they just said write them a zero and I was like well okay and but there were other bits  in that same register that I needed to interact with and without realizing I did a read a modify  and then a write to turn those bits on you know x or equals 27 or something right and it turns out  that although these bits were always write zero according to the datasheet you always had to write  to them when you would read from them sometimes they would not give you zeros and so you could  by doing that read modify write inadvertently write non-zero values back to these reserved bits  which just so happened to cause the chip to flip into a factory test mode and I lost two days of  my life on a stupid read modify write problem and that had I had the discipline back then to do this  then that would have not it would not have been possible for me to get around masking those bits  off because it would have in this case this is a reader you'll see in the write function it would  always force those bits back to zero before they went out on the wire and so I wouldn't have to  worry about that anymore yeah so here's the writer function it's the very same concept takes the value  that I want to put into that register rips off the reserved bit field to make sure they're always  set to zeros before it writes it out and this low-level function here we haven't written yet  we'll come back to that in a little bit but but that by having the discipline to use that function  there'd be no way those reserved bits could ever get set to anything but zero unless you know  lucky meteor strike or something like that in which case that's probably the least of your problems  right but um there actually was a minor bug in your code so pop quiz anybody see it yeah it's  actually the single underscores reserved from the compiler double underscores as well  all right so triple underscore I don't care  yeah but it's a double underscore with another underscore right  yeah thank you yeah the name it's yeah the paste operation is yeah that's the that's the correct  term for it which out so no that's not the bug actually yeah I don't I guess if compilers are  actually going to eat the whole double underscore space for themselves and linux is pretty much  screwed because lots of code in the linux kernel it uses that namespace and so  so yeah well  gcc appears to get it right enough yeah the actual problem in this case is a subtle one but it  this sort of vigilance is such a part of my core that when I made the mistake with this slide  I left it in here as a reminder to no one else but me see the value that's actually getting written  out is this value v here which we've ripped off the reserve bits and all that part's correct  but if we compile this thing in debug mode the value that we're that we're printing out to the logs  is prior to this reserved masking and so the value we present in the debugging log might not be the  one that actually goes out on the wire so I don't if you'd call that a bug or a feature I don't know  but so really you need to take this line and move it up there so all right so that's just a small bug  but in the bigger picture what we're trying to do here is go away from well that looked a lot less  green on my laptop a little while ago is is go from a sort of a code soup which is what you have  with most device drivers that I see and ultimately end up having to rewrite where you've got multiple  paths to where you can actually get data out onto the wire to talk to the tip to just a few that we  have to correct to check and although if you actually look at the object file we'll have one of these  little functions for every single control register in the system so there'll be a hundred of them  there's only one place the code was actually written and so that's the only place we have to  check to make sure it's correct and so this is a lot easier to check for correctness than this I  would think the dashed line here was just sort of a mnemonic for okay if I'm in these functions  I have to have that spin lock held it's for a different a different subject matter but the same  idea and you can see there's really a nasty bug if you would actually do a function diagram like  this for a lot of device drivers you tend to see behavior like this where well it briefly dropped  the lock it briefly for example unlocked the spin lock and then took it again is that because these  operations here are really independent of each other or did it actually need to stay locked there  and he implemented it incorrectly well because of we're gonna as you'll see in a couple minutes  we're gonna move the locking functionality to a place where it's unavoidable then we can guarantee  that our system always looks like that and so it's just one less thing we have to deal with  so that's the the basic reads and writes are usually pretty simple and so it's not usually  a question but if you want to take a shot at it okay and don't right yeah go ahead this question  was why have a why not have a parameterized function why have a separate function for each  register because what I have what I've found when I do it like that is that I end up with a lot more  redundant code in here and so I suppose that it may be the case that I was solving a problem where  those redundancies were unavoidable and so the macro I ended up writing here was larger than it  really could have been but when when I finally decided that this is the way that I would do things  yeah it eats up additional space a little bit more space for each the function call overhead  but it turns out the macro itself that you end up writing the amount of code you write is about  as small as you can make it and so in in my case the objective was to write as few lines of code  as possible and get as much functionality from them as possible and that's how I ended up with this  you see in a in a couple of minutes we can get some of the behavior of that parameterization  without actually having to write it and that's what we're gonna we're gonna go to in just a  second here but anyone else yeah the offer yeah that's yeah that's a fairly recent addition  and it it it claims to do a lot of the same kind of stuff for you and in fact it'll do the caching  and and things like that for you as well for registers that are supposed to be read only and  write only because it's although it's it seems like it's if it's your kernel version the very  latest kernel version it's it's going to be available sort of across the board and a lot of  the kernel versions I work with the api is not there yet because it's fairly recent and  that's recent for embedded stuff yeah three years but but in the same way sometimes I need control  over this low-level stuff in a way that I don't get with that with that api and sometimes it's  just heavier than I want so although you could incorporate use of that api into the ideas  that I'm talking about here it's not really this presentation I was trying to stay focused on the  low-level stuff so I think that's an interesting api if you're working with a really recent kernel  but I haven't banged away on it enough to know if it solves the kind of problems I'm wanting to  solve here or not but that's a good point all right so just interacting with the ordinary read  and write registers and reserve bit fields is kind of a corner case the really interesting stuff  starts to happen when you're manipulating the bit fields of the of the of these individual  control registers because then you've got things like read modify writes and shifting and so forth  that you can get wrong and this would be a pretty traditional example of what I'm talking about here  when we're going to set that sampling rate of the chip they read out the control register  fiddle with the bits that they need to and then write them back out again very traditional  and this is one of those examples where the register mapping api might not be all that much  help because it's not going to give you this tight level of low level interaction control over  what's actually going back and forth on the wire to the same degree that you might have here but  like I said I'd I probably don't know enough about that api to make a blanket statement like that  but it won't stop me right so because I've got the microphone but anyway so this is very traditional  code but one of the things that that troubles me greatly about this code is the magic numbers here  which seems like if we were going to do that whole enumeration to describe the register file why do  we still have to have a magic number here we must have missed something somewhere and yeah we'll  fix that in just a second but the other thing we can fix is well convert it so that it's using the  actual register accessors instead of banging away on the raw low level you know register function  itself so we get code that when we're interacting with this control register it's unavoidable those  reserve bits can't leak out into us and when we do the read modify write the write down here  is always stripping the reserve bits off so they can't leak back in so just switching to using our  register accessor or higher level functions here was a small improvement but it's not really the  improvement I'm after because I want to get rid of this thing here because this just to me screams  like if I'm going to come around and modify that in some way later I'm going to get something wrong  and I'm kind of doing work that the compiler should be doing for me and at least in the Linux  kernel there's a function for that what we were trying to do here is move move this bit field  that was passed as a parameter into its final location in the control register right  and Linux will do that for you if you ask it to and that's why I use the enumeration with that  mask for the ray field so if you go back in and I should have had that slide if you go backwards  here to the to the definition of that macro or that enumeration that was the describing the bits  that are associated with that rate bit field in the control register since those bits are all clumped  together we can look at that as kind of a human and figure out how many bits we need to shift  the incoming value into right and well you know we can have there's a function for that in Linux  it's a macro called FFS and in most cases the FFS macro is a single assembly language instruction  and and actually in a statement like this the compiler can probably figure it out  and so while we have something here that looks like actual code we got we didn't get rid of the  need excuse me we didn't get rid of the need for the shift because somebody somewhere has to do  that to interact with with that bit field but what we did do is move the documentation back up into  the enumeration and where it's now authoritative again and so if there's something wrong with  the rate setting we know the problem isn't here the problem is up in our expression of the register  file our description of the register file so i'm getting i'm getting logic out of this place  and and and trying to get it to a location where i can inspect it and and have confidence that it's  correct in just that one location and so this works for for bit fields and where where the bits are  crunched together tightly into one control register there are plenty of examples of cases where  like for example in some of the avr's timer counters where well they needed three bits of  scaling and two of the bits are in this register and one of them is in in that register over there  i don't know how else to deal with that except to do the code manually something that looks like  this but still by going through the register accessors you get the masking and so forth is correct  so you just you don't have to think about it anymore so there's that any questions on that  okay so we've cleaned up our bit fields a little bit i think my batteries must be dead but  our ever vigilant bug detector is tripped again and i don't know if you guys saw it or not and that  is that um well although i can't really do it my naming convention uh for functions that have  leading double underscores in the driver code is that you have to have the lock that's associated  with a chip held before you call into this function and so this function bma set rate since it doesn't  have that leading underscore assumes that uh uh that the lock is not held when you call into  this function but i'm not taking the lock in here anywhere and i've got this horrible not horrible  necessary read modify write that if it were to get interrupted by another interaction with that  control register bad things might happen right so the bug here is that there's no locking  and we need locking because that read modify write operation so the the only answer well there's two  answers i guess one is throw the double underscore on do it and then make it the caller's problem to  take the lock before he calls in which makes sense in some cases um but in this case i'm going to  just throw in uh my locking function here which um i can make this an inline so it's a spin lock  irq save or whatever i want but the locking mechanism itself since the signature would be the same  throughout my driver i don't have to check it to make sure that it's correct later and since the  function name here is going to be very regular you you can see a pattern developing here where you  might be able to make some of this into a macro template of some kind as well but that kind of  gets off the the beaten path so so that's dealing with bit fields um there's another class of special  case here and that is registers that you can only write to and you can't read from yeah there's a  few of them out there um or sometimes there's a register that when you write to it it gives you a  different behavior than when you read from it it's like when you write to it it's you're clearing the  interrupt status when you read from it it's telling you the interrupt status but it doesn't actually  clear it those sorts of things and you can use this approach that i'm talking about here to make  those registers behave a little more regularly so that you don't have to pay attention to that  sort of uh what would you call it cognitive dissonance between well if you know my description  of this chip is changing whether i'm reading or writing it and um and also it turns out some of  this stuff makes it easier when you start doing runtime power management makes it actually makes  it easier to do the rest of the driver and when i talk about runtime power management here i'm  talking about the kernels runtime p.m. api where it makes it really super brain dead easy to if i  have an accelerometer that i'm reading every 100 millisecond and it only takes me a millisecond  to power it up take a measurement power it back down why would i leave it powered up the other  99 milliseconds linux is perfectly happy to just power it up and down when i need it and and and  actually i don't have to pay attention to that myself or that's auto suspend functionality  linux will just turn it on when it detects that it needs to be turned on and then turn it back off  when it detects nobody's using it and i don't have to worry about that code at all and this code can  help make some of that stuff a little easier to work and so so this is in this example i've got  this range register which is uh is it a one g a two g a 10 g or a bazillion g uh filter on the  accelerometer and it's it's um it's right only meaning you can't read it um so once you store  the value into the range register if you want to put that value back later like when you've power  cycled the chip during this suspend and resume somebody somewhere has to keep track of this  thing's value and well we really don't want to have to do it ourselves in the code because  eventually someone will forget but since we got this perfectly obvious spot here to do it the  place to do it would be in the function that interacts with the range register right so  so this is the original register writing template if you will and the only thing i've added here  is this switch statement that says okay if the register that he's writing to is the range register  then save a copy of it away so that we can get it back out later and what's nifty about this is  at least with a gnu compiler and it's pretty straightforward i would expect most compilers  to do this when you actually print out copies of this function if you print out the where the name  is the range register then you get this code up here plus only this statement the bma range equals  v and all that other stuff gets optimized away um and if you uh like so for example in this case  i have a second one that's for the reset register i'll come back to that in just a second that  code's not there except when you express this macro for the reset uh register name and if you're  talking about neither of those registers the optimizer takes this whole switch statement out so  if you go in and look in the assembly language you're actually getting exactly the code that you  would write yourself even though it looks like i'm going to have a switch statement in every  single copy of this function the optimizer takes it out because that's we're switching on something  that's constant so the optimizer can figure out where it's going to go and it just gets rid of the  rest of the code oh yeah before i forget if you guys want to copy this presentation afterwards  just send me an email and i'll i'll send it to you because uh i guess here they don't have a link  for you to download it all or anything so so this one has saved me um quite a few times because  you'll see in a couple of minutes how i handle the range function but it turns out in the  datasheet there's this little footnote on the bottom of page 996 right after the index that says  anytime you interact with the reset register you got to wait for four milliseconds and uh  gosh it'd be awfully nice if i could guarantee that well i can anytime i write to the reset register  there's a use sleep statement right here for four milliseconds and as long as i have the  program or discipline to when i interact with the reset register to use this function it's  unavoidable so i can't forget it so i can't race ahead of the chip by mistake just because i forgot  to add that extra line in my code so it's just one less thing i need to think about so now that  we've got this range parameter this this bma structure is the structure you guys are already  using to keep track of your device driver data it's the thing you malloc and your probe function and  and so on and so in the right function we just stashed a copy of it away here in our read function  well we can't actually read from that because it was a write only register so we're just going to  return the value that we cashed away as a return result and so uh then uh that way when we write  the value that we get back is always the value that we wrote to it we don't have to pay attention  to uh saving it away because the function that i just showed you saved it for us right so but  you can probably guess yeah so some of you are actually still awake yeah i've got kind of got  i said that the range register was was uh was write only but here we did the read before the  switch i got the the order reversed right so i like to throw those little pop quizzes in there  because it's very gratifying when someone goes yeah i saw that yeah okay yeah there's no lock in  this case but in this case the locking the lack of locking is okay because i am writing all these  very these are like the last mile functions and i'm assuming the lock is already held anytime you  call into these functions and so um yeah and so the reason then in this case i used uh the u  sleep here that depending on your platform that's probably a bug because if you take that if you  call that function with a spin lock the system can't actually sleep it's got to actually sit  there and spin and there's two there's two types of delays and the linux kernel ones that spin and  ones that actually will do a a weight over like a context which and uh and i think that actually  needs to be a u delay not a u sleep so that you get the one that just sits there and busy waits  because if you were to take that with a with a spin lock held the system on a single core machine  anyway might get very unhappy with you anyway so that's the so that gets us um making sure that  we're interacting with that reset register correctly we're interacting with the range  register correctly and so now in the code anytime we interact with this reset register it's unavoidable  we'll take that four millisecond weight um and so in our suspend and resume functionality um while  we can in fact manually write the the uh the cash value back out to the registers when we're you  know setting the state of the chip back to how it was before um we could also in our suspend logic  just to a register read save and a register write and the read method doesn't actually do anything  for the range register because it's not readable but our code could have that very regular pattern  to it regardless and so and then so i like to sort of combine these macros and and so for registers  that are readable and writable i'll have a uh an enclosing one that says that okay so this reset  register is readable and writable the range register is write only the x-axis lsp register is read only  chip id is read only because if i do that if i do it like that then um although it wasn't necessary  in this case because it handled a read correctly you just saw that on the previous slide if i were  to actually do it like this then there's no bma reg read range anywhere in the system so i can catch  that mistake at compile time and so um if i try and do a read from in some cases that's a better  approach than having a different simulated behavior for the range register to have no just never read  from it and we can we can check authoritatively that we're not reading from it because the code will  actually compile and link um where if we take this approach here but if we if we were to make it  readable and write and writable or readable quote and writable quote by simulating the read operation  then we can make it look like that instead so that's that okay so that's the basics of the very  lowest level accessors and and the motivation behind them was even even if we're going to have 150  of these registers we don't want to write 150 copies of the same code but we want to make sure  all the code is not only correct but that we can all see it on a page and verify that it's correct  and so that was a motivation for all that stuff we're going to take it to a slightly higher level  now in just a second but are there any questions yeah yeah um this question was what if you have to  read from more than one register and um how do you handle the locking associated with that to make  sure that you don't get interrupted um well there's I guess I'll refine the question slightly in in  Linux there's no way to make sure that you don't get interrupted from this line and this line but  there's a way to make sure that the system can't run the code between these two concurrently right  and that's so you could have other unrelated code running on that processor over there or an interrupt  handler could interrupt you and so forth but really what you're talking about is um in in this in our  case we were using this lock here to throttle access to the chip as a whole so anytime you're  interacting with the control registers of the chip you take this lock and while you hold this lock if  somewhere else in the system someone reads from a sysfs attribute that's trying to export one of  these register values like I'm going to show you in a second they got to wait until you put the lock  back so as long as you hold this lock you can read and write the registers as long as you want  without fear that anyone else gets into the chip and interacts with you at the same time  so that's where my naming convention becomes important is that um I try and remind myself that  look anytime I'm manipulating with these control registers I'm going to assume that the lock is  held um and if I don't see a call somewhere above it to the locking function I'll be very suspicious  of that code but for the reasons you use site it doesn't make sense in most cases to put the  locking inside of this function because that would close off your ability to interact with multiple  registers without interruption does that answer your question?  right yeah so exactly yeah yeah so I might yeah I might have one function that returns an x y and z  data structure so it's got the whole vector yeah so I'd have a that function would take the lock  read from x read from y read from z and usually in the data sheet it'll tell you if you read  them in this order even the hardware is inside the accelerometer is locking the register values  so they're all tightly coupled in time and and so you read all three of those and then unlock  and uh yeah does that answer your question to your satisfaction? all right yeah I guess at that  point I punt to it's a manual a manual operation but if if you if it is something in the case of x y  reading x y's and z's you you probably would put that in a function so you'd have all that logic  in one place in using the same sort of strategy that's here so all right so but so now that we  now that we can be sure we're interacting with these control registers within the driver itself  I have often very often found it really nice to be able to sort of snoop on the hardware while it's  running to see what's going on in fact that cranky lte modem example I opened the presentation with  was an example of that the only thing I added was this was this ability right here and it gave me  the ability to say oh well the reason the chip isn't talking to us is because something put it to sleep  and uh I was able to do that by looking at the states of some of its status registers and so  forth and say oh the chip is asleep well it's only like four places in the driver that put the chip  to sleep so we can go look at those and it gives you a lot of insight into what's going on so  usually when a client comes to me and says I want a device driver I say yeah we'll get to that the  very first thing I give them is this kind of functionality one of these sysfs attribute files  for every control register in the chip because usually the chip is uh although it may be really  complicated they usually don't have to use all the complicated parts to make sure that the hardware  is actually working correctly because well you know if you don't get a chip id and a version  result that you're expecting odds are you put the chip in rotated 90 degrees from the way it's  supposed to be and you can answer those kinds of questions without writing a whole device driver  so wouldn't it be nice if we could just barf up code that look like this  uh really really easily and in fact I can do it in about five minutes because I can just copy paste  the macros that you just saw and copy paste a couple of macros compile and I'm good to go and so  if you've got all this stuff correct like we talked about earlier ultimately this is the  code that you want to have written which is this is a sysfs attribute a show method so this is  when a user reads from that interface this would be the code that gets involved and this code's  going to look exactly the same for each one of those control registers it's readable and and the  pattern is take the lock get the data release the lock format the data send it back a very regular  pattern here but we don't want to copy paste 150 copies of this code we want the compiler to do that  for us and um and in fact there's actually another thing that's wrong with this code as well  that I just kind of skimmed over and that is we're assuming that the chip we're talking to is on i  squared c and that might not be the case because the bma 250 can come in either flavor and we don't  want to have a compiler a flag or something here that says oh read it from i squared c or read it  from spi because the driver doesn't know the platform integrator the guy who put the chip on  the board is the only one who knows the driver shouldn't have to care and so there's a media  assumption baked in here that we want to get rid of as well and so i'm going to throw templates at  this as well and usually at this point my clients would go really do we really want a hundred you  know when you do an ls and that's just directory we have 150 values show up and I go well yeah  probably don't want all 150 of them but the one that you tell me to leave out is going to be the  one you want six months from now so just put them all in there it doesn't take take any space at all  so so the macro that we had for our register reading logic we're just going to take that same  that same sort of strategy here the first is start using the methods that we've already written  so that that part of the system is that part of the implementation is correct and then just commit  the rest of it to a template there looks like this this is the the right version instead of the read  version but the idea is the same so when a user echoes a value and directs the output into that  cysfs attribute we end up here we take the string and format it according to so it's a cool capability  of this string to unsigned long function here where you tell it the radix so am i expecting it in  in hexadecimal or decimal and i suppose you could modify the name of the function here in some way  because you might want to be able to convey to the user oh it's going to be in hexadecimal if you go  in this interface or decimal if you go in this one but i just usually just tell them it's always in  hexadecimal because that's the way the hardware thinks and so um so i don't i don't pay much  attention to that but so so we do the string conversion here and then uh wow we're this is  joy of a new presentation um and then take the lock do the read or in this case the write and  then release the lock on the next slide all very straightforward stuff and we're going to use the  switch statement just like we did before which is if you're talking about the power of the reset  register that code will be there if you're not talking about either of those the optimizer  takes all that code out and look we can make it so that it's unavoidable when they write to the  reset register we wait that four milliseconds but we don't actually need that here because it's baked  into this function right here so we can get rid of that special case entirely and um the power  register was was for a related example more related to runtime power management but so that's the idea  and so the default case here is just an ordinary read and then we unlock and return so that's  the same sort of pattern as before yeah i know i'm way out over on time and so okay so any questions  on that all right so just to sort of skim ahead to some of the syntax well it's a good thing you  can download this right um one of the things you can check is is the locking being done correctly  i use this macro quite a bit which is uh the spin spin locks have a try lock api that says  is you can test and see if it's locked already and if it is it returns an error code and so  inside of these functions you could even have a debugging mode that checks did they actually  take the lock correctly if not kick out an error message um using the the try lock functionality  like that and then i just gather these things up uh and then into this uh in in this example it's  readable and writable so it expresses the read and write low level register accessors prints out the  attribute show and store methods and then creates the attribute structure associated with them and  so this one line of c code that i write plus the 10 or 12 lines of those of those defines ends up  becoming uh multiplied by 150 registers a whole bunch of code but we didn't write it and so and  what's more most that code fits on one page so we can make sure that it's correct so then uh this  is the part that i haven't figured out how to automate yet which is i still haven't figured out  how to make the compiler print this stuff out for me but i'm not sure that i really want to but  so you gather this stuff up into an attribute structure and you pass it to the sysfs create  group function but one of the nice things about not having it automated is you can sort of selectively  take out the parts that the customer doesn't know he's going to want yet right and um or you can  so you can do it at compile time which is not my preference my preference is something a little  more dynamic like in a module variable and uh right um so the media agnostic part i'll just skip ahead  skim skim skim because the sysfs attributes there was nothing media specific in there it just takes  an ordinary device structure which is at the core of an spi device i swear it's the device whatever  so this this attribute doesn't care the reason it cares is because we put this code in here  where it shouldn't be and that's where you get to the uh to the lowest level helper function that i've  been skipping over until now my data structure for the for the bma controller has uh pointers for  both an i2c client and an spi device and uh because in my probe function i did a kz alec the one that  i'm not using is going to be set to null and so in my low level read function here i can check oh am  i using i2c if yes just do it over i2c otherwise if it's spi do it otherwise i have no idea how i  got here and so and then there's nothing in linux that says so this would be your i2c probe uh function  there's the kz alec and we said of all this will be familiar to anyone who's written i2c driver  and then i just hand that off to a sort of a generic probe function right and from then on  that's uh that's the logic for for connecting the chip up it's all baked in that function there the  probe function for i2c is literally just 10 lines of code and there's nothing in linux that says you  can't have um so the probe function is going to do the usual stuff there's nothing in linux that says  you can't have multiple drivers registered in the same kernel module linux doesn't care kernel modules  are a thing all by themselves and so uh so in cases like this bosh bma 250 that supports i2c and spi  i'd register both the drivers because the only difference the only delta then in the memory  footprint is going to be a very small amount of code for the data structure the very short spi probe  function and i2c function and um and then whatever's in the device tree is going to define which one  actually gets invoked because it isn't until the device tree gets parsed that linux calls  one or the other of the probe functions and the logic for the as long as the logic for the  chip is the same in both cases the only code that's different is i've got this stub function for i2c  which does nothing other than initialize the data structure and then pass it over to a generic  sort of mea agnostic implementation and then likewise for spi and so then at that point you've  got a driver that you don't even have to recompile when you switch to the spi from the i2c version  you have a driver that in a couple of pages of code you can guarantee that you're interacting with  all 150 of those control registers correctly all the time every time and that you have something  that you can interact with from the command line to keep an eye on the hardware and actually drive  the hardware in some cases and you didn't have to write 150 times the amount of code that uh  with 150 times the amount of bugs in it so unless you get paid to debug it's not really my thing but  it's like that dillbert cartoon all right so any questions then i guess we probably ought to take  them outside if there are any i'll be around uh i like email my emails is on the slides too so if  you want to copy the presentation i'm happy to send it to you i'll answer questions and stuff as well  so thank you very much and sorry i ran over
Linux_Device_Driver_Best_Practices_-_Bill_Gatliff_0_1703.png Linux_Device_Driver_Best_Practices_-_Bill_Gatliff_1704_1775.png Linux_Device_Driver_Best_Practices_-_Bill_Gatliff_1776_8495.png Linux_Device_Driver_Best_Practices_-_Bill_Gatliff_8496_9911.png Linux_Device_Driver_Best_Practices_-_Bill_Gatliff_9912_9971.png Linux_Device_Driver_Best_Practices_-_Bill_Gatliff_9972_23447.png Linux_Device_Driver_Best_Practices_-_Bill_Gatliff_23448_24635.png Linux_Device_Driver_Best_Practices_-_Bill_Gatliff_24636_25127.png Linux_Device_Driver_Best_Practices_-_Bill_Gatliff_25128_26531.png Linux_Device_Driver_Best_Practices_-_Bill_Gatliff_26532_27011.png Linux_Device_Driver_Best_Practices_-_Bill_Gatliff_27012_32099.png Linux_Device_Driver_Best_Practices_-_Bill_Gatliff_32100_32759.png Linux_Device_Driver_Best_Practices_-_Bill_Gatliff_32760_33707.png Linux_Device_Driver_Best_Practices_-_Bill_Gatliff_33708_36167.png Linux_Device_Driver_Best_Practices_-_Bill_Gatliff_36168_38015.png Linux_Device_Driver_Best_Practices_-_Bill_Gatliff_38016_38147.png Linux_Device_Driver_Best_Practices_-_Bill_Gatliff_38148_41051.png Linux_Device_Driver_Best_Practices_-_Bill_Gatliff_41052_41579.png Linux_Device_Driver_Best_Practices_-_Bill_Gatliff_41580_42167.png Linux_Device_Driver_Best_Practices_-_Bill_Gatliff_42168_42383.png Linux_Device_Driver_Best_Practices_-_Bill_Gatliff_42384_43091.png Linux_Device_Driver_Best_Practices_-_Bill_Gatliff_43092_43367.png Linux_Device_Driver_Best_Practices_-_Bill_Gatliff_43368_45011.png Linux_Device_Driver_Best_Practices_-_Bill_Gatliff_45012_45455.png Linux_Device_Driver_Best_Practices_-_Bill_Gatliff_45456_46163.png Linux_Device_Driver_Best_Practices_-_Bill_Gatliff_46164_46403.png Linux_Device_Driver_Best_Practices_-_Bill_Gatliff_46404_49079.png Linux_Device_Driver_Best_Practices_-_Bill_Gatliff_49080_52055.png Linux_Device_Driver_Best_Practices_-_Bill_Gatliff_52056_53867.png Linux_Device_Driver_Best_Practices_-_Bill_Gatliff_53868_53927.png Linux_Device_Driver_Best_Practices_-_Bill_Gatliff_53928_54023.png Linux_Device_Driver_Best_Practices_-_Bill_Gatliff_54024_54071.png Linux_Device_Driver_Best_Practices_-_Bill_Gatliff_54072_55703.png Linux_Device_Driver_Best_Practices_-_Bill_Gatliff_55704_58955.png Linux_Device_Driver_Best_Practices_-_Bill_Gatliff_58956_59183.png Linux_Device_Driver_Best_Practices_-_Bill_Gatliff_59184_59399.png Linux_Device_Driver_Best_Practices_-_Bill_Gatliff_59400_59519.png Linux_Device_Driver_Best_Practices_-_Bill_Gatliff_59520_59567.png Linux_Device_Driver_Best_Practices_-_Bill_Gatliff_59568_60083.png Linux_Device_Driver_Best_Practices_-_Bill_Gatliff_60084_60131.png Linux_Device_Driver_Best_Practices_-_Bill_Gatliff_60132_62675.png Linux_Device_Driver_Best_Practices_-_Bill_Gatliff_62676_62891.png Linux_Device_Driver_Best_Practices_-_Bill_Gatliff_62892_64007.png Linux_Device_Driver_Best_Practices_-_Bill_Gatliff_64008_65015.png Linux_Device_Driver_Best_Practices_-_Bill_Gatliff_65016_65063.png Linux_Device_Driver_Best_Practices_-_Bill_Gatliff_65064_65123.png Linux_Device_Driver_Best_Practices_-_Bill_Gatliff_65124_67367.png Linux_Device_Driver_Best_Practices_-_Bill_Gatliff_67368_72155.png Linux_Device_Driver_Best_Practices_-_Bill_Gatliff_72156_73271.png Linux_Device_Driver_Best_Practices_-_Bill_Gatliff_73272_73331.png Linux_Device_Driver_Best_Practices_-_Bill_Gatliff_73332_73583.png Linux_Device_Driver_Best_Practices_-_Bill_Gatliff_73584_74195.png Linux_Device_Driver_Best_Practices_-_Bill_Gatliff_74196_74255.png Linux_Device_Driver_Best_Practices_-_Bill_Gatliff_74256_74951.png Linux_Device_Driver_Best_Practices_-_Bill_Gatliff_74952_74999.png Linux_Device_Driver_Best_Practices_-_Bill_Gatliff_75000_76235.png Linux_Device_Driver_Best_Practices_-_Bill_Gatliff_76236_78215.png Linux_Device_Driver_Best_Practices_-_Bill_Gatliff_78216_79127.png Linux_Device_Driver_Best_Practices_-_Bill_Gatliff_79128_79223.png Linux_Device_Driver_Best_Practices_-_Bill_Gatliff_79224_79271.png Linux_Device_Driver_Best_Practices_-_Bill_Gatliff_79272_83387.png Linux_Device_Driver_Best_Practices_-_Bill_Gatliff_83388_83435.png Linux_Device_Driver_Best_Practices_-_Bill_Gatliff_83436_83483.png Linux_Device_Driver_Best_Practices_-_Bill_Gatliff_83484_85523.png Linux_Device_Driver_Best_Practices_-_Bill_Gatliff_85524_86435.png Linux_Device_Driver_Best_Practices_-_Bill_Gatliff_86436_86591.png Linux_Device_Driver_Best_Practices_-_Bill_Gatliff_86592_87335.png Linux_Device_Driver_Best_Practices_-_Bill_Gatliff_87336_87899.png Linux_Device_Driver_Best_Practices_-_Bill_Gatliff_87900_88019.png Linux_Device_Driver_Best_Practices_-_Bill_Gatliff_88020_88247.png Linux_Device_Driver_Best_Practices_-_Bill_Gatliff_88248_89555.png Linux_Device_Driver_Best_Practices_-_Bill_Gatliff_89556_90671.png Linux_Device_Driver_Best_Practices_-_Bill_Gatliff_90672_91019.png Linux_Device_Driver_Best_Practices_-_Bill_Gatliff_91020_91139.png Linux_Device_Driver_Best_Practices_-_Bill_Gatliff_91140_91847.png Linux_Device_Driver_Best_Practices_-_Bill_Gatliff_91848_91895.png Linux_Device_Driver_Best_Practices_-_Bill_Gatliff_91896_92747.png Linux_Device_Driver_Best_Practices_-_Bill_Gatliff_92748_92903.png Linux_Device_Driver_Best_Practices_-_Bill_Gatliff_92904_93131.png Linux_Device_Driver_Best_Practices_-_Bill_Gatliff_93132_93239.png Linux_Device_Driver_Best_Practices_-_Bill_Gatliff_93240_93491.png Linux_Device_Driver_Best_Practices_-_Bill_Gatliff_93492_93647.png Linux_Device_Driver_Best_Practices_-_Bill_Gatliff_93648_93707.png Linux_Device_Driver_Best_Practices_-_Bill_Gatliff_93708_93755.png Linux_Device_Driver_Best_Practices_-_Bill_Gatliff_93756_93803.png Linux_Device_Driver_Best_Practices_-_Bill_Gatliff_93804_94223.png Linux_Device_Driver_Best_Practices_-_Bill_Gatliff_94224_95303.png Linux_Device_Driver_Best_Practices_-_Bill_Gatliff_95304_95675.png Linux_Device_Driver_Best_Practices_-_Bill_Gatliff_95676_95747.png Linux_Device_Driver_Best_Practices_-_Bill_Gatliff_95748_95939.png Linux_Device_Driver_Best_Practices_-_Bill_Gatliff_95940_96755.png Linux_Device_Driver_Best_Practices_-_Bill_Gatliff_96756_96803.png Linux_Device_Driver_Best_Practices_-_Bill_Gatliff_96804_97163.png Linux_Device_Driver_Best_Practices_-_Bill_Gatliff_97164_98871.png
Good morning. My name is Bill Gatliff and I guess I'll be in a moment of honesty. I'll be terrified. I'm terrified to meet you, but I am grateful for the chance to be here. Oh, that's that is a hot mic. Okay. Yeah, so if I get too loud, let me know. First time in Oslo, first time at NDC, I've been involved with embedded systems conferences in America for more than 10 years. So this isn't the first time I've been on a stage. It's the first time here in Oslo. And like I said, I'm grateful for that opportunity. I am not a professional speaker. If you haven't figured that out already, I'm a developer just like most of you. And I spend a very small amount of my total time doing this. The rest of the time I'm locked away in a cave underneath my house writing code and so forth. So so what we're going to talk about in this presentation is all the time I've spent in that cave, making the same mistakes over and over again, I finally got wise one day and said, you know, maybe I should write some of this stuff down and try and come up with something that works better the next time around. And so when I'm using the term best practices here, what I'm really talking about is things that are best practices for the kind of work that I do for the kind of problems I solve for for my type of workflow. I'm a freelancer. I've been doing embedded Linux since about 1999. And most of the time I spend doing what I call high complexity device drivers, device drivers where it goes beyond just making an LED blink and reading a push button. One of my more recent clients made cell phones and we did some work on the accelerometers and touch screens and so forth for them. I've also done work with a company that does basically for you drill a hole like you're going to use to drill oil. And then this machine, which is I guess truck mounted, parks over that hole and drops a small explosive charge into the well. And then it measures the shock wave that comes back. And from that information, it can get inside into the condition of the well and a bunch of I don't I'm not a geologist. I heard the part that oh, there's going to be stuff that explodes. Okay, I'm in. And so for that one, I had to do an ADD converter that was running on a hardware time clock of one kilohertz. But we had to do direct memory transfer into user memory so that he could do a real time graph as the shock wave was echoing around inside the well. And so those kinds of things. So we were not only were we doing DMA, we were doing DMA directly into user space from the ADD converter itself with no intermediate hop inside kernel space like a traditional driver does. And we were able to get tons of throughput that way. And all of this stuff here that I'm about to show you is stuff that isn't just a good idea for me. It's the way I do my work now. And so when a client calls me and says we need a device driver, I usually say, well, let's talk about that part in a second, because we don't even know yet what your hardware looks like, whether your hardware actually works or anything, those more important considerations. Before we start talking about what do you want the interface to that hardware to look like? Because in the case of that ADD converter example, the interface for it was basically a memory pointer that said, here's where I want you to put the data. And this is the number of samples I want you to give me go. And so it didn't have a traditional device node with an open, closed, read and write type interface, even though in Linux everything is a file, so to speak. And so, but that was the interface that helped us solve our problem. And I usually tell people that your job as a device driver author is mostly to babysit the hardware and make the application writer's job as easy as possible. What I mean by that is on the hardware side, you have to maintain control of whatever this chip is to prevent it from either running away with the system or not, you know, stops doing what it's supposed to do. And on the application side, you don't ever want to reveal to the authors of those applications how the hardware works. You always want to model the information that comes out of the hardware because that gives you usually that additional degree of separation gives you the ability to do things like change the hardware later, test the hardware with maybe even like a test-driven development kind of thing. Just throw that out there since he's right there. The guy that wrote the book on it, at least for him better to work and those sorts of things. So, and these are all techniques that I found that are helpful in that regard. This is not my first time to this part of the world. I had a client who took me to Malmo in Sweden for three weeks to troubleshoot a cranky LTE modem on a prototype handset and you can probably figure out who they were just from that information alone. They gave me about three weeks to do the work and so basically by about Wednesday of the first week, the first thing I did was sort of on Monday afternoon when I showed up, we did sort of a stop work and said, let's just throw some of this stuff into the system so that we can get inside into what the hardware is doing. By Wednesday, we knew what the problem was, we knew what the solution was and so I spent the next two and a half weeks doing that and it was really nice. But what I'm going to show you may not work for you but hopefully it'll make it some ideas going and the idea is if you find something that's truly better than what I'm doing here, we should all be stealing it from, stealing those kinds of ideas from each other because the main reason I stand up here is not because it gives me sort of a quasi-free trip to this part of the world a couple of times a year but it's really more personal than that. I have this recurring nightmare where I wake up in the dream anyway, I wake up in the hospital after some horrific accident plugged into a machine that I designed and that nightmare is bad enough but there's one that's slightly worse than that and that's awakening being dependent for life support on a machine that I didn't design and so what I'm trying to do here is if we all steal the best ideas from each other then that crap floor if you will just gets higher and higher and we all benefit from that so some day immortality is on the line here, right? So if I can get this slide to go in the right direction. So the general thinking for the kind of work that I do anyways like this, client calls me because they either have new hardware or they have a problem and they want me to solve it and so my thinking is usually along the lines of not if it's going to break but when it breaks what are the things I want to have in place to troubleshoot the system at that point. This question of a client coming to me saying I want a device driver, I said well you know that's really a question for me to answer for you not I mean yeah you're gonna need some kind of device driver at the end of the day or you wouldn't be calling me but the nature of that at the initial point of contact is usually much less important than hey let's make sure the hardware actually works and it turns out that that hardware works the hooks that you need in place to help validate that the hardware is working correctly you can get them in fairly quickly and then talk about the device driver stuff in parallel with them actually checking out the hardware itself and so that kind of stuff of what I would like to have present in the system can be done very quickly usually just in a couple of days even for the most complicated stuff but I want to I want to take an approach here that is such that we don't all the all the code that we're writing here is code that is safe enough that we can leave it in the production system because what's deployed to me might not be deployed to the customer you know I get off my desk and it goes back into their cubicle and I'm not necessarily done with it at that point because two months from now I may be having to visit this hardware again to figure out this new problem that they've created or they transferred out to whoever their customer is in the same situation over and over again so whatever it is that if it works well for us during this initial development phase of the project it'd be nice if we could do it in such a way that we could leave those tools available to us through the entire products lifecycle but our implementations can't do anything to make the problem any worse than it already is and so what I'm about to show you here are some things that it'll look kind of strange to you in spots you think well he's kind of really over complicated a few things but it's motivated by the fact of there's certain things I want to have but I don't want to trade off overall system quality to get them and so the general themes then for that are that my clicker is not cooperating today okay is that you'll see in a few minutes since this is a Linux device drivers class and most Linux is written in C I'm going to use the compiler in a lot of cases to do a lot of work that you guys are probably doing on your own right now which is a problem because there's a direct correlation between the number of lines you have code you write and the amount of debugging you'll do later and so anytime you can get a line of code out of your system it's always a good thing now it doesn't mean we should make the system more and more stupid so that we can make it safer and more correct now what I'm saying is we need to stop writing some some lines of code we shouldn't be writing we should be letting the system write for us and I'll show what I mean in a few minutes and but that's the idea here because what I've found is if we if we hand off certain development processes to the tools that were designed to do them for us not only will they do it more correctly but we can now work at the speed of thought and by that I mean I don't have to get bogged down with wealth the problem is with a data structure interaction or definition or something I can stay focused on that without having to get side tracked by the well as the interrupt handler for spin locks working and things like that because we already know in other in other parts of the system that we've tested that and it works and we know that it's going to continue work because nothing's changed just because we're working over another data file is that making it how many of you guys actually write device drivers awesome how many of you just don't want to and you're here because there's no better class there's a couple of you all right the honest ones anyway okay so if you've ever written a device driver in Linux then eventually you come around to this idea that you never tell Linux how to do its job right when you need a locking mechanism you ask Linux for one a spin lock or mutex or whatever the correct mechanism is and the same way it goes for when you want to power a chip up or down you never tell Linux what regulator to turn on and off you tell Linux please turn on whatever regulator if any it is that's connected to my VN pin for example and you make Linux sort out the details of okay what regulator is that if I'm going to turn that node in the circuit on I'm gonna have to power this up and power this up and power this up you let Linux figure all that stuff out and and and through experience you probably learned that the reason you do that is because if Linux understands enough of your schematic that it can power up voltage regulators in the correct order then it probably understands enough about your schematic that it can do other things correctly as well like making sure that it doesn't turn the lights out in that part of the circuit until it's told the driver that's responsible for this part of the circuit that it's going to do that those sorts of things and so when you can tell Linux only what you need done and Linux does it correctly that's usually a good sign that Linux understands your schematic enough that it can help you do your job and so so that's a factor in some of in the code that you'll see here another thing that you'll see reflected here quite a bit is how many of you read Alejandro Rubini's book called Linux device drivers all three editions right it's a good book but it's got to be one of the worst titles in the history of books and the reason I say that is because if you flip through every page of that book you see there's almost no discussion about device drivers as Linux defines them because in Linux you're not doing a device driver unless you're working with a data structure that's called device underscore driver nothing else matters and in Linux device drivers are all about power management they don't know about semaphores they don't know about c devs they don't know about miss devices those are interface implementations or synchronization primitives or interrupt handling there's no correlation between interrupt handling and device drivers except when you write the code that hooks the two of them together they're completely isolated concepts to Linux and when you really embrace that and say okay I'm not going to do interface related work in my device driver code and I'm not going to do device driver work in my interface related code all of a sudden a lot of your code gets a lot simpler because you're not telling Linux how to do its job you're just telling Linux what needs to be done and Linux can always do that better than you can I'll come back to that more in just a few minutes but just to excuse me to just kind of get back to the material at hand here the strategies that I'm going that you'll see in this code here is first of all since I'm a hardware guy I'm a device driver guy which means I'm I kind of got one foot in both grave right in each grave I want to be able to see what the hardware is doing from the programming environment and I also want to write code that I can move to another platform without having to rewrite a single line of in fact in some cases not even having to recompile it and that's actually possible even though a lot of the code that I see today won't do that and I'll show you some examples I'm not going to have my device driver working as an interface I'm not going to have my interface code working as a device driver and by that I mean it's very tempting inside of the open implementation for your device node well that's when I'll power the chip up and so as soon as using application opens the interface that's when I'll know that it's time to do something well while it's true that you may it may be that in your system the the correct time to power the chip up is when the user application calls that open method that doesn't mean your implementation in the file operations structure for the open functionality includes powering the chip up now what you do instead is you tell Linux to please if the chip's not turned on take it take it through its resume sequence in the same way as if they had just powered the system up from scratch or the system is just awakening from a from a suspend state or something like that and because if you if you put that code only where it belongs then you you've you've just taken out the chunk of code that I'll have to come back and write later that is well you know in their application they weren't doing runtime power management so they're not powering this chip on and off 10 times a second for their own reasons in addition to interacting with that open interface so we got to get that code out of there and and over to the right place and Linux can do that kind of stuff all day long so but if to just focus on the hardware parts there's enough to do right there because we need to make sure that we don't that when the datasheet says don't touch a register value we don't touch it and that we when the datasheet says to ignore a register value or a bit field we ignore it at all times and we want to make sure that we do that correctly every single time we interact with the chip and so you can imagine that well what what bill is really after here is adding a lot of error handling to our code and the answer is yes but no because that code clearly has to exist somewhere but you don't have to write it and and there's an app that will write it for you and it's called your C compiler and your brain and I'll show you in just a minute so but once you've got that stuff going it's kind of nice to be able to just sort of log into the system and then from the command line at least keep an eye on what the hardware is doing by being able to interact with those control registers if it's an SPI or an I2C chip and even some platform devices have control registers that are memory map that have interesting values that you'd want to see from the command line so but not just not just those guys there's you know USB has a similar model PCI all these guys and there's plenty of chips out there especially chips for things like touch controllers and GPIO controllers and so forth that have dual interface support where it's depending on how you wire one of the pins on the package it'll talk over SPI or I2C but the logic the behavioral model if you will the chip is exactly the same in both cases and if the chip hasn't changed any if the only thing that's changed is the interface we're using to talk to the chip why would we write the code again but that tends to be the case well it's I wrote the I2C driver now I need the SPI version so what are you going to start over now it's the only thing that's changed is the media transport layer if you will and so that's the only code that you should have to add for the new chip and and so that's the way that'll be part of this presentation as well so but so if there any questions at this point anyone anyone out in the overflow room okay hearing no objections we'll go ahead and start from the bottom and that is the low level the low level stuff that the code that Linux gives you to interact with peripheral control registers no matter where they are in the system and if you've done device drivers for Linux you've seen all these before memory mapped registers registers that are attached to an I2C bus registers that are attached to an SPI bus and so on and so on and so on there are models for these everywhere and at the end of the day you have to do this code somewhere but you don't want to do it any more than you have to and so and most developers know this and so when they have a need to take the chip through a reset operation for example they don't just necessarily throw that code into the four or five places in their in their module where they need that reset functionality they'll put it all into one function and then just call that function and why do you think it is they do that I'm sorry it's easy to read the code and make sure you've done it correctly right and and and and yeah so we already know some of the stuff that I'm going to be talking about is if this is the only place in the system where we do a reset and we have a problem with a reset we know right where to go look for the problem in the code because this is the only places and I'm just taking that that idea to the next level there's a little bit of nuance you'll see in a couple minutes here was some of the examples here are inspired by a Bosch BMA 250 accelerometer which is kind of cool because it's got a accelerometer and a rate gyro built into the same chip and it also has a separate power control register that has the various bits in it it's shared between both functionalities and then it also has this kind of cool bus interface matching logic where it can act like an an I squared C adapter so you can hook another chip usually a compass on the back end of it and the way you model all that stuff to Linux is is part of the Bosch chip you model as an I2C driver for the accelerometer a different one for the gyro you hook the power management logic in as a regulator that appears to be attached to both of those and that pass-through bus they call it the ox bus just looks like another I squared C adapter to Linux and so it looks like you actually written four or five different device drivers because you're interacting with four or five different device structures but what you've actually done is given Linux complete insight into into how all the different pieces of that chip fit together so that when Linux the text that well this application over here wants the gyro but he doesn't need the accelerometer the logic to turn on just the gyro and leave the accelerometer in whatever state it's in is already there and in the case of later on when you see me having a function like this rate here there's a control register in this chip that has a bit field called rate where you set the sampling rate for the chip and I've kind of oversimplified it a little bit because it's a little more there's a little more than just a few bits associated with it but the idea here is that at some point we need to manipulate that bit field in this control register but that's not what this this function is really that's not the service that this function is providing to the rest of the driver this is just set the sample rate of the chip and and then in the same way that we hid the logic of what specifically it means to do a reset what specifically it means to adjust the sampling rate for the chip is hidden inside of this function here and that the distinction of that will be a little clearer in just a minute but just as a look ahead so but we don't want to write code like I just saw like you just saw over and over and over again because we need to do these same sorts of things everywhere and every time that is making sure we interact with each one of these control registers correctly and that we can verify that we've done it correctly by inspection and the way the way I approach that so this would be the first best practice recommendation most of the drivers you see they they use something like a define for they'll usually have a macro for each one of the control registers that has the base address of the of the register in the in the devices register file not really changing that idea what I am going to do though is put in an enumeration instead for reasons you'll see in a couple of minutes and I'm also going to adopt a very regular naming convention here which is the name is exactly the same as it appears in the datasheet to the greatest extent possible and so that'll that'll go on for a while but in addition to this is the part that tends to be new in addition to having the address of the register I'm also going to have a regular convention for expressing the bits in that register that I'm not allowed to touch and in this case for example in the LSB the x-axis register the BMA 250 has two 8-bit registers one for the upper 8 bits and one for the lower 8 bits and the lower 8 bits most of those bits are invalid so 3e if you picture that bit field in your head you'll see it's just the lower EF I don't know it's just a few number of bits so the only ones you're paying attention to there and and I will will do that for every single control register in the system and have a regular naming convention which in my case is the name of the register underscore underscore reserved I have a very regular naming convention like that I will sit down and do this before I do any other code in the system because what you're actually doing here is you're describing the register file and each one of those bit fields to the compiler and I'll show you why that's that's helpful in just a second so this chip ID has no reserve bits they're all valid this x-axis LSB has a few reserve bits that's the the mask there and whether the convention is positive or negative is up to you and then the control one register here has a rate field that's these that these bits right here a one and an eight so they're kind of right in the middle of the byte and so so I sit down and that that transcribes that that table that's usually in the data sheet for the chip into its equivalent c code expression and then at the end of the day this is the code that we want to end up writing but we don't want to write it ourselves but this is code that'll be very very familiar to you if you've written device drivers before this is a a method for interacting with what's called a device attribute which are things you can see from the command line in the sys file system and the logic in this implementation so this is a we're going to interact allow the user to read out the value in the x-axis LSB register and so we'll take a lock and hold a lock so that nothing else is manipulating the chip while we're using it and then we'll just read the x-axis LSB and format it for them pretty straightforward stuff my motivation in the long run is to have this functionality for every register in the chip which might be a hundred or a hundred and fifty registers but if I write this code myself I'm going to get it wrong somewhere and and what's worse is if I write this code and and do get it wrong it's going to be a real pain in the neck to go fix it in all 150 places where this code appears so this is really is more pseudo code to me as a functional functional description of what I want even though this code would compile as is so I'm not going to write this code by hand I'm going to use c macros for that and if you've ever used these macros in c you'd be pretty off of the mainstream as far as most c programmers don't know what the double hash preprocessor operator does so I'll just explain it here I'm going to create a macro called BMA register read and it'll take a single argument which is the name of the register and anywhere you see hash hash and then that argument that parameter name there it will replace with whatever the the word was that you put in there when you invoke this macro and and so this code is for doing the low-level register read we'll come back and add the locking and so forth in a minute but this is for the low-level register read when I use this macro it's it's going to print a function called BMA reg read and then whatever the the name is there which will be as follows it'll it'll call a low-level single function called register read and pass it the enumeration that's associated with that register name and then the return result it will mask with that reserved bit field and then it will return that and so now you can see why I had the enumeration and that's because I want to make sure that these things get these tokens get expanded in the correct order I want the preprocessor to expand this but not until it knows the meanings of these things here and so if you get the order wrong and you're using macros instead of enumerations it doesn't work and so by using the enumerations you make sure that the preprocessor has an actual definition for this that's correct when it gets ready to express all this thing here so really the only thing that's novel about this is that instead of writing and copy pasting 150 of these one for each control register I write the code once and then I make the compiler write it for me 150 times and the way I would do that is just have one of this literally in my c code right below this this pound-defined statement usually I'll have a line for each one of the control registers and every time I have a statement that looks like this this is actually creating a function called bma read register x axis lsb that is the function's name and so well okay for chip i see if I was a professional speaker I would have used chip id as the example but you get the idea when I want to interact when I want to read the chip id I don't I don't do an i2c sm bus read byte right in that location in the code I call this little helper function here that that the compiler wrote for me called bma reg read chip id and that function would be this code but I only wrote this code once you're following so far okay so this isn't this isn't generally too difficult this is the x-axis lsb example here and we could you know put these together and have a higher level well I really don't ever want to see the x and y axis axes apart from each other and so I could have a bma read x-axis but there's no register named x-axis that would be a different function these are just a low level register accessor type functions because at this point the problem we were we were focused on solving is making sure that anytime we interact with any control register in the system we are minding the reserved bits and this will guarantee that that's the case as long as we have the programmer discipline to always interact with the control registers through that helper function we don't have to worry about reserved bits anymore I worked with the Hitachi sh2 part a few years ago and it had a dma controller that I was using that had a couple of bits in it that were marked as reserved and they just said write them a zero and I was like well okay and but there were other bits in that same register that I needed to interact with and without realizing I did a read a modify and then a write to turn those bits on you know x or equals 27 or something right and it turns out that although these bits were always write zero according to the datasheet you always had to write to them when you would read from them sometimes they would not give you zeros and so you could by doing that read modify write inadvertently write non-zero values back to these reserved bits which just so happened to cause the chip to flip into a factory test mode and I lost two days of my life on a stupid read modify write problem and that had I had the discipline back then to do this then that would have not it would not have been possible for me to get around masking those bits off because it would have in this case this is a reader you'll see in the write function it would always force those bits back to zero before they went out on the wire and so I wouldn't have to worry about that anymore yeah so here's the writer function it's the very same concept takes the value that I want to put into that register rips off the reserved bit field to make sure they're always set to zeros before it writes it out and this low-level function here we haven't written yet we'll come back to that in a little bit but but that by having the discipline to use that function there'd be no way those reserved bits could ever get set to anything but zero unless you know lucky meteor strike or something like that in which case that's probably the least of your problems right but um there actually was a minor bug in your code so pop quiz anybody see it yeah it's actually the single underscores reserved from the compiler double underscores as well all right so triple underscore I don't care yeah but it's a double underscore with another underscore right yeah thank you yeah the name it's yeah the paste operation is yeah that's the that's the correct term for it which out so no that's not the bug actually yeah I don't I guess if compilers are actually going to eat the whole double underscore space for themselves and linux is pretty much screwed because lots of code in the linux kernel it uses that namespace and so so yeah well gcc appears to get it right enough yeah the actual problem in this case is a subtle one but it this sort of vigilance is such a part of my core that when I made the mistake with this slide I left it in here as a reminder to no one else but me see the value that's actually getting written out is this value v here which we've ripped off the reserve bits and all that part's correct but if we compile this thing in debug mode the value that we're that we're printing out to the logs is prior to this reserved masking and so the value we present in the debugging log might not be the one that actually goes out on the wire so I don't if you'd call that a bug or a feature I don't know but so really you need to take this line and move it up there so all right so that's just a small bug but in the bigger picture what we're trying to do here is go away from well that looked a lot less green on my laptop a little while ago is is go from a sort of a code soup which is what you have with most device drivers that I see and ultimately end up having to rewrite where you've got multiple paths to where you can actually get data out onto the wire to talk to the tip to just a few that we have to correct to check and although if you actually look at the object file we'll have one of these little functions for every single control register in the system so there'll be a hundred of them there's only one place the code was actually written and so that's the only place we have to check to make sure it's correct and so this is a lot easier to check for correctness than this I would think the dashed line here was just sort of a mnemonic for okay if I'm in these functions I have to have that spin lock held it's for a different a different subject matter but the same idea and you can see there's really a nasty bug if you would actually do a function diagram like this for a lot of device drivers you tend to see behavior like this where well it briefly dropped the lock it briefly for example unlocked the spin lock and then took it again is that because these operations here are really independent of each other or did it actually need to stay locked there and he implemented it incorrectly well because of we're gonna as you'll see in a couple minutes we're gonna move the locking functionality to a place where it's unavoidable then we can guarantee that our system always looks like that and so it's just one less thing we have to deal with so that's the the basic reads and writes are usually pretty simple and so it's not usually a question but if you want to take a shot at it okay and don't right yeah go ahead this question was why have a why not have a parameterized function why have a separate function for each register because what I have what I've found when I do it like that is that I end up with a lot more redundant code in here and so I suppose that it may be the case that I was solving a problem where those redundancies were unavoidable and so the macro I ended up writing here was larger than it really could have been but when when I finally decided that this is the way that I would do things yeah it eats up additional space a little bit more space for each the function call overhead but it turns out the macro itself that you end up writing the amount of code you write is about as small as you can make it and so in in my case the objective was to write as few lines of code as possible and get as much functionality from them as possible and that's how I ended up with this you see in a in a couple of minutes we can get some of the behavior of that parameterization without actually having to write it and that's what we're gonna we're gonna go to in just a second here but anyone else yeah the offer yeah that's yeah that's a fairly recent addition and it it it claims to do a lot of the same kind of stuff for you and in fact it'll do the caching and and things like that for you as well for registers that are supposed to be read only and write only because it's although it's it seems like it's if it's your kernel version the very latest kernel version it's it's going to be available sort of across the board and a lot of the kernel versions I work with the api is not there yet because it's fairly recent and that's recent for embedded stuff yeah three years but but in the same way sometimes I need control over this low-level stuff in a way that I don't get with that with that api and sometimes it's just heavier than I want so although you could incorporate use of that api into the ideas that I'm talking about here it's not really this presentation I was trying to stay focused on the low-level stuff so I think that's an interesting api if you're working with a really recent kernel but I haven't banged away on it enough to know if it solves the kind of problems I'm wanting to solve here or not but that's a good point all right so just interacting with the ordinary read and write registers and reserve bit fields is kind of a corner case the really interesting stuff starts to happen when you're manipulating the bit fields of the of the of these individual control registers because then you've got things like read modify writes and shifting and so forth that you can get wrong and this would be a pretty traditional example of what I'm talking about here when we're going to set that sampling rate of the chip they read out the control register fiddle with the bits that they need to and then write them back out again very traditional and this is one of those examples where the register mapping api might not be all that much help because it's not going to give you this tight level of low level interaction control over what's actually going back and forth on the wire to the same degree that you might have here but like I said I'd I probably don't know enough about that api to make a blanket statement like that but it won't stop me right so because I've got the microphone but anyway so this is very traditional code but one of the things that that troubles me greatly about this code is the magic numbers here which seems like if we were going to do that whole enumeration to describe the register file why do we still have to have a magic number here we must have missed something somewhere and yeah we'll fix that in just a second but the other thing we can fix is well convert it so that it's using the actual register accessors instead of banging away on the raw low level you know register function itself so we get code that when we're interacting with this control register it's unavoidable those reserve bits can't leak out into us and when we do the read modify write the write down here is always stripping the reserve bits off so they can't leak back in so just switching to using our register accessor or higher level functions here was a small improvement but it's not really the improvement I'm after because I want to get rid of this thing here because this just to me screams like if I'm going to come around and modify that in some way later I'm going to get something wrong and I'm kind of doing work that the compiler should be doing for me and at least in the Linux kernel there's a function for that what we were trying to do here is move move this bit field that was passed as a parameter into its final location in the control register right and Linux will do that for you if you ask it to and that's why I use the enumeration with that mask for the ray field so if you go back in and I should have had that slide if you go backwards here to the to the definition of that macro or that enumeration that was the describing the bits that are associated with that rate bit field in the control register since those bits are all clumped together we can look at that as kind of a human and figure out how many bits we need to shift the incoming value into right and well you know we can have there's a function for that in Linux it's a macro called FFS and in most cases the FFS macro is a single assembly language instruction and and actually in a statement like this the compiler can probably figure it out and so while we have something here that looks like actual code we got we didn't get rid of the need excuse me we didn't get rid of the need for the shift because somebody somewhere has to do that to interact with with that bit field but what we did do is move the documentation back up into the enumeration and where it's now authoritative again and so if there's something wrong with the rate setting we know the problem isn't here the problem is up in our expression of the register file our description of the register file so i'm getting i'm getting logic out of this place and and and trying to get it to a location where i can inspect it and and have confidence that it's correct in just that one location and so this works for for bit fields and where where the bits are crunched together tightly into one control register there are plenty of examples of cases where like for example in some of the avr's timer counters where well they needed three bits of scaling and two of the bits are in this register and one of them is in in that register over there i don't know how else to deal with that except to do the code manually something that looks like this but still by going through the register accessors you get the masking and so forth is correct so you just you don't have to think about it anymore so there's that any questions on that okay so we've cleaned up our bit fields a little bit i think my batteries must be dead but our ever vigilant bug detector is tripped again and i don't know if you guys saw it or not and that is that um well although i can't really do it my naming convention uh for functions that have leading double underscores in the driver code is that you have to have the lock that's associated with a chip held before you call into this function and so this function bma set rate since it doesn't have that leading underscore assumes that uh uh that the lock is not held when you call into this function but i'm not taking the lock in here anywhere and i've got this horrible not horrible necessary read modify write that if it were to get interrupted by another interaction with that control register bad things might happen right so the bug here is that there's no locking and we need locking because that read modify write operation so the the only answer well there's two answers i guess one is throw the double underscore on do it and then make it the caller's problem to take the lock before he calls in which makes sense in some cases um but in this case i'm going to just throw in uh my locking function here which um i can make this an inline so it's a spin lock irq save or whatever i want but the locking mechanism itself since the signature would be the same throughout my driver i don't have to check it to make sure that it's correct later and since the function name here is going to be very regular you you can see a pattern developing here where you might be able to make some of this into a macro template of some kind as well but that kind of gets off the the beaten path so so that's dealing with bit fields um there's another class of special case here and that is registers that you can only write to and you can't read from yeah there's a few of them out there um or sometimes there's a register that when you write to it it gives you a different behavior than when you read from it it's like when you write to it it's you're clearing the interrupt status when you read from it it's telling you the interrupt status but it doesn't actually clear it those sorts of things and you can use this approach that i'm talking about here to make those registers behave a little more regularly so that you don't have to pay attention to that sort of uh what would you call it cognitive dissonance between well if you know my description of this chip is changing whether i'm reading or writing it and um and also it turns out some of this stuff makes it easier when you start doing runtime power management makes it actually makes it easier to do the rest of the driver and when i talk about runtime power management here i'm talking about the kernels runtime p.m. api where it makes it really super brain dead easy to if i have an accelerometer that i'm reading every 100 millisecond and it only takes me a millisecond to power it up take a measurement power it back down why would i leave it powered up the other 99 milliseconds linux is perfectly happy to just power it up and down when i need it and and and actually i don't have to pay attention to that myself or that's auto suspend functionality linux will just turn it on when it detects that it needs to be turned on and then turn it back off when it detects nobody's using it and i don't have to worry about that code at all and this code can help make some of that stuff a little easier to work and so so this is in this example i've got this range register which is uh is it a one g a two g a 10 g or a bazillion g uh filter on the accelerometer and it's it's um it's right only meaning you can't read it um so once you store the value into the range register if you want to put that value back later like when you've power cycled the chip during this suspend and resume somebody somewhere has to keep track of this thing's value and well we really don't want to have to do it ourselves in the code because eventually someone will forget but since we got this perfectly obvious spot here to do it the place to do it would be in the function that interacts with the range register right so so this is the original register writing template if you will and the only thing i've added here is this switch statement that says okay if the register that he's writing to is the range register then save a copy of it away so that we can get it back out later and what's nifty about this is at least with a gnu compiler and it's pretty straightforward i would expect most compilers to do this when you actually print out copies of this function if you print out the where the name is the range register then you get this code up here plus only this statement the bma range equals v and all that other stuff gets optimized away um and if you uh like so for example in this case i have a second one that's for the reset register i'll come back to that in just a second that code's not there except when you express this macro for the reset uh register name and if you're talking about neither of those registers the optimizer takes this whole switch statement out so if you go in and look in the assembly language you're actually getting exactly the code that you would write yourself even though it looks like i'm going to have a switch statement in every single copy of this function the optimizer takes it out because that's we're switching on something that's constant so the optimizer can figure out where it's going to go and it just gets rid of the rest of the code oh yeah before i forget if you guys want to copy this presentation afterwards just send me an email and i'll i'll send it to you because uh i guess here they don't have a link for you to download it all or anything so so this one has saved me um quite a few times because you'll see in a couple of minutes how i handle the range function but it turns out in the datasheet there's this little footnote on the bottom of page 996 right after the index that says anytime you interact with the reset register you got to wait for four milliseconds and uh gosh it'd be awfully nice if i could guarantee that well i can anytime i write to the reset register there's a use sleep statement right here for four milliseconds and as long as i have the program or discipline to when i interact with the reset register to use this function it's unavoidable so i can't forget it so i can't race ahead of the chip by mistake just because i forgot to add that extra line in my code so it's just one less thing i need to think about so now that we've got this range parameter this this bma structure is the structure you guys are already using to keep track of your device driver data it's the thing you malloc and your probe function and and so on and so in the right function we just stashed a copy of it away here in our read function well we can't actually read from that because it was a write only register so we're just going to return the value that we cashed away as a return result and so uh then uh that way when we write the value that we get back is always the value that we wrote to it we don't have to pay attention to uh saving it away because the function that i just showed you saved it for us right so but you can probably guess yeah so some of you are actually still awake yeah i've got kind of got i said that the range register was was uh was write only but here we did the read before the switch i got the the order reversed right so i like to throw those little pop quizzes in there because it's very gratifying when someone goes yeah i saw that yeah okay yeah there's no lock in this case but in this case the locking the lack of locking is okay because i am writing all these very these are like the last mile functions and i'm assuming the lock is already held anytime you call into these functions and so um yeah and so the reason then in this case i used uh the u sleep here that depending on your platform that's probably a bug because if you take that if you call that function with a spin lock the system can't actually sleep it's got to actually sit there and spin and there's two there's two types of delays and the linux kernel ones that spin and ones that actually will do a a weight over like a context which and uh and i think that actually needs to be a u delay not a u sleep so that you get the one that just sits there and busy waits because if you were to take that with a with a spin lock held the system on a single core machine anyway might get very unhappy with you anyway so that's the so that gets us um making sure that we're interacting with that reset register correctly we're interacting with the range register correctly and so now in the code anytime we interact with this reset register it's unavoidable we'll take that four millisecond weight um and so in our suspend and resume functionality um while we can in fact manually write the the uh the cash value back out to the registers when we're you know setting the state of the chip back to how it was before um we could also in our suspend logic just to a register read save and a register write and the read method doesn't actually do anything for the range register because it's not readable but our code could have that very regular pattern to it regardless and so and then so i like to sort of combine these macros and and so for registers that are readable and writable i'll have a uh an enclosing one that says that okay so this reset register is readable and writable the range register is write only the x-axis lsp register is read only chip id is read only because if i do that if i do it like that then um although it wasn't necessary in this case because it handled a read correctly you just saw that on the previous slide if i were to actually do it like this then there's no bma reg read range anywhere in the system so i can catch that mistake at compile time and so um if i try and do a read from in some cases that's a better approach than having a different simulated behavior for the range register to have no just never read from it and we can we can check authoritatively that we're not reading from it because the code will actually compile and link um where if we take this approach here but if we if we were to make it readable and write and writable or readable quote and writable quote by simulating the read operation then we can make it look like that instead so that's that okay so that's the basics of the very lowest level accessors and and the motivation behind them was even even if we're going to have 150 of these registers we don't want to write 150 copies of the same code but we want to make sure all the code is not only correct but that we can all see it on a page and verify that it's correct and so that was a motivation for all that stuff we're going to take it to a slightly higher level now in just a second but are there any questions yeah yeah um this question was what if you have to read from more than one register and um how do you handle the locking associated with that to make sure that you don't get interrupted um well there's I guess I'll refine the question slightly in in Linux there's no way to make sure that you don't get interrupted from this line and this line but there's a way to make sure that the system can't run the code between these two concurrently right and that's so you could have other unrelated code running on that processor over there or an interrupt handler could interrupt you and so forth but really what you're talking about is um in in this in our case we were using this lock here to throttle access to the chip as a whole so anytime you're interacting with the control registers of the chip you take this lock and while you hold this lock if somewhere else in the system someone reads from a sysfs attribute that's trying to export one of these register values like I'm going to show you in a second they got to wait until you put the lock back so as long as you hold this lock you can read and write the registers as long as you want without fear that anyone else gets into the chip and interacts with you at the same time so that's where my naming convention becomes important is that um I try and remind myself that look anytime I'm manipulating with these control registers I'm going to assume that the lock is held um and if I don't see a call somewhere above it to the locking function I'll be very suspicious of that code but for the reasons you use site it doesn't make sense in most cases to put the locking inside of this function because that would close off your ability to interact with multiple registers without interruption does that answer your question? right yeah so exactly yeah yeah so I might yeah I might have one function that returns an x y and z data structure so it's got the whole vector yeah so I'd have a that function would take the lock read from x read from y read from z and usually in the data sheet it'll tell you if you read them in this order even the hardware is inside the accelerometer is locking the register values so they're all tightly coupled in time and and so you read all three of those and then unlock and uh yeah does that answer your question to your satisfaction? all right yeah I guess at that point I punt to it's a manual a manual operation but if if you if it is something in the case of x y reading x y's and z's you you probably would put that in a function so you'd have all that logic in one place in using the same sort of strategy that's here so all right so but so now that we now that we can be sure we're interacting with these control registers within the driver itself I have often very often found it really nice to be able to sort of snoop on the hardware while it's running to see what's going on in fact that cranky lte modem example I opened the presentation with was an example of that the only thing I added was this was this ability right here and it gave me the ability to say oh well the reason the chip isn't talking to us is because something put it to sleep and uh I was able to do that by looking at the states of some of its status registers and so forth and say oh the chip is asleep well it's only like four places in the driver that put the chip to sleep so we can go look at those and it gives you a lot of insight into what's going on so usually when a client comes to me and says I want a device driver I say yeah we'll get to that the very first thing I give them is this kind of functionality one of these sysfs attribute files for every control register in the chip because usually the chip is uh although it may be really complicated they usually don't have to use all the complicated parts to make sure that the hardware is actually working correctly because well you know if you don't get a chip id and a version result that you're expecting odds are you put the chip in rotated 90 degrees from the way it's supposed to be and you can answer those kinds of questions without writing a whole device driver so wouldn't it be nice if we could just barf up code that look like this uh really really easily and in fact I can do it in about five minutes because I can just copy paste the macros that you just saw and copy paste a couple of macros compile and I'm good to go and so if you've got all this stuff correct like we talked about earlier ultimately this is the code that you want to have written which is this is a sysfs attribute a show method so this is when a user reads from that interface this would be the code that gets involved and this code's going to look exactly the same for each one of those control registers it's readable and and the pattern is take the lock get the data release the lock format the data send it back a very regular pattern here but we don't want to copy paste 150 copies of this code we want the compiler to do that for us and um and in fact there's actually another thing that's wrong with this code as well that I just kind of skimmed over and that is we're assuming that the chip we're talking to is on i squared c and that might not be the case because the bma 250 can come in either flavor and we don't want to have a compiler a flag or something here that says oh read it from i squared c or read it from spi because the driver doesn't know the platform integrator the guy who put the chip on the board is the only one who knows the driver shouldn't have to care and so there's a media assumption baked in here that we want to get rid of as well and so i'm going to throw templates at this as well and usually at this point my clients would go really do we really want a hundred you know when you do an ls and that's just directory we have 150 values show up and I go well yeah probably don't want all 150 of them but the one that you tell me to leave out is going to be the one you want six months from now so just put them all in there it doesn't take take any space at all so so the macro that we had for our register reading logic we're just going to take that same that same sort of strategy here the first is start using the methods that we've already written so that that part of the system is that part of the implementation is correct and then just commit the rest of it to a template there looks like this this is the the right version instead of the read version but the idea is the same so when a user echoes a value and directs the output into that cysfs attribute we end up here we take the string and format it according to so it's a cool capability of this string to unsigned long function here where you tell it the radix so am i expecting it in in hexadecimal or decimal and i suppose you could modify the name of the function here in some way because you might want to be able to convey to the user oh it's going to be in hexadecimal if you go in this interface or decimal if you go in this one but i just usually just tell them it's always in hexadecimal because that's the way the hardware thinks and so um so i don't i don't pay much attention to that but so so we do the string conversion here and then uh wow we're this is joy of a new presentation um and then take the lock do the read or in this case the write and then release the lock on the next slide all very straightforward stuff and we're going to use the switch statement just like we did before which is if you're talking about the power of the reset register that code will be there if you're not talking about either of those the optimizer takes all that code out and look we can make it so that it's unavoidable when they write to the reset register we wait that four milliseconds but we don't actually need that here because it's baked into this function right here so we can get rid of that special case entirely and um the power register was was for a related example more related to runtime power management but so that's the idea and so the default case here is just an ordinary read and then we unlock and return so that's the same sort of pattern as before yeah i know i'm way out over on time and so okay so any questions on that all right so just to sort of skim ahead to some of the syntax well it's a good thing you can download this right um one of the things you can check is is the locking being done correctly i use this macro quite a bit which is uh the spin spin locks have a try lock api that says is you can test and see if it's locked already and if it is it returns an error code and so inside of these functions you could even have a debugging mode that checks did they actually take the lock correctly if not kick out an error message um using the the try lock functionality like that and then i just gather these things up uh and then into this uh in in this example it's readable and writable so it expresses the read and write low level register accessors prints out the attribute show and store methods and then creates the attribute structure associated with them and so this one line of c code that i write plus the 10 or 12 lines of those of those defines ends up becoming uh multiplied by 150 registers a whole bunch of code but we didn't write it and so and what's more most that code fits on one page so we can make sure that it's correct so then uh this is the part that i haven't figured out how to automate yet which is i still haven't figured out how to make the compiler print this stuff out for me but i'm not sure that i really want to but so you gather this stuff up into an attribute structure and you pass it to the sysfs create group function but one of the nice things about not having it automated is you can sort of selectively take out the parts that the customer doesn't know he's going to want yet right and um or you can so you can do it at compile time which is not my preference my preference is something a little more dynamic like in a module variable and uh right um so the media agnostic part i'll just skip ahead skim skim skim because the sysfs attributes there was nothing media specific in there it just takes an ordinary device structure which is at the core of an spi device i swear it's the device whatever so this this attribute doesn't care the reason it cares is because we put this code in here where it shouldn't be and that's where you get to the uh to the lowest level helper function that i've been skipping over until now my data structure for the for the bma controller has uh pointers for both an i2c client and an spi device and uh because in my probe function i did a kz alec the one that i'm not using is going to be set to null and so in my low level read function here i can check oh am i using i2c if yes just do it over i2c otherwise if it's spi do it otherwise i have no idea how i got here and so and then there's nothing in linux that says so this would be your i2c probe uh function there's the kz alec and we said of all this will be familiar to anyone who's written i2c driver and then i just hand that off to a sort of a generic probe function right and from then on that's uh that's the logic for for connecting the chip up it's all baked in that function there the probe function for i2c is literally just 10 lines of code and there's nothing in linux that says you can't have um so the probe function is going to do the usual stuff there's nothing in linux that says you can't have multiple drivers registered in the same kernel module linux doesn't care kernel modules are a thing all by themselves and so uh so in cases like this bosh bma 250 that supports i2c and spi i'd register both the drivers because the only difference the only delta then in the memory footprint is going to be a very small amount of code for the data structure the very short spi probe function and i2c function and um and then whatever's in the device tree is going to define which one actually gets invoked because it isn't until the device tree gets parsed that linux calls one or the other of the probe functions and the logic for the as long as the logic for the chip is the same in both cases the only code that's different is i've got this stub function for i2c which does nothing other than initialize the data structure and then pass it over to a generic sort of mea agnostic implementation and then likewise for spi and so then at that point you've got a driver that you don't even have to recompile when you switch to the spi from the i2c version you have a driver that in a couple of pages of code you can guarantee that you're interacting with all 150 of those control registers correctly all the time every time and that you have something that you can interact with from the command line to keep an eye on the hardware and actually drive the hardware in some cases and you didn't have to write 150 times the amount of code that uh with 150 times the amount of bugs in it so unless you get paid to debug it's not really my thing but it's like that dillbert cartoon all right so any questions then i guess we probably ought to take them outside if there are any i'll be around uh i like email my emails is on the slides too so if you want to copy the presentation i'm happy to send it to you i'll answer questions and stuff as well so thank you very much and sorry i ran over
when I'm using the term best practices here, what embedded Linux since about 1999. And most of the or anything, those more important considerations. don't ever want to reveal to the authors of those that hardware works the hooks that you need in now work at the speed of thought and by that I only what you need done and Linux does it drivers except when you write the code that hooks programming environment and I also want to write system and if you've done device drivers for Linux complete insight into into how all the that reserved bit field and then it will return Linux will do that for you if you ask it to and register writing template if you will and the from in some cases that's a better approach than anytime you're interacting with the control out how to make the compiler print this stuff out