 Thank you for coming and have a fun for us them So Yes, yes No, this one great. All right. I'm going to talk about The embedded controller on Google's Chromebooks today and how you can use them in your own projects Now you might have seen I work for National Instruments. I don't work for Google and you might be like Why does this guy talk about that? so In my team we built software defined radios, so they use our piece. There's an entire track at foster them on software radio people using the radio to do that and I kind of do the embedded part for our embedded products It's a very small team within National Instruments. So we're kind of the open source weirdos, but I try to work upstream and The way I ended up with this talk basically was we had the previous product and It had a microcontroller and it was an A tiny and it was miserable because you couldn't update the firmware really well and There's like let's not put microcontrollers anymore on our products if we need to if we don't need to and Well, then my hardware designer shows up and he's like, hey, we're gonna put a microcontroller in the next product And I mean, oh, no So I do what everyone does that does open source, right? You're lazy. You look around And and you see can you find open source project? It's doing your work for you So sort of how I ended up here I looked around they found a Chromebooks. It was totally random I ran into that that they have an embedded controller from where it's open source So About Chromebooks Well There's they're not so special. They're like just laptops basically. Sorry for the marketing slides It's just an x86 or an arm v7 or arm v8 lately It runs Linux, which is great because you can also run normal Linux in it. You don't need to use Chrome OS They have a user land which is somewhat derived from Gen 2 like long time ago. They forked it and did their own thing and It's basically a very small laptop like that one Super lightweight and everything that has lots of battery life and also an embedded controller, so If you take take them apart That that's a PCB of one of the older ones. It's an aces and it has a celeron brussel and It also has an embedded controller from microchip and you can see in green and That's hooked up by LPC and you can actually bite at microcontroller. So I was intrigued. They're like, huh Something I can buy that runs after that I can find on the internet. Awesome. I can probably use that Another one that's like the big brother of this one has a rock chip in there a bit more Random that one and also has a microcontroller In this case, it's hooked up via SPI So fun fact if you if you look at it the actual SOC is down here and the microcontroller is over here So if you compare the size of the microcontroller with the size of the application processor It's pretty interesting. I find anyway so Let's look a bit about this firmware that I've been talking about Well It's three gloss beastie which is Nice, I mean could be worse It's currently supports a bunch of MCUs that you can readily buy they're usually m0s m3s and 4s So for my project I picked the m0 because it's the cheapest one There's kind of odd architectures they support So from what I figured out the way Google sort of does the whole Chromebook thing is they they build reference boards and Then vendors like high sense like that one or aces goes ahead and takes that reference board and Changes them around a little bit and turns that into a Chromebook. So So often you have firmware that's the same for all of the Chromebooks of The Veyron series, which would be that one for example, whoo I already learned last year I had dark slides that didn't work. So I changed that to be white Anyway, and they also say you should use kernel coding style Which is bonus points in my book because you don't get eye cancer like from a lot of embedded firmware So yeah, you can just check out the code. It's under website and I'm what there's like zero documentation. So That's kind of the downside of the whole thing Which is why I figured having this talk is actually quite useful There's one more talk that one of the Google guys gave about the subject at their firmware summit but that's From 2014 so pretty outdated So if you look at the source you basically have a board directory where you have board specific code so a board would be a single Chromebook you have Chip where you basically find stuff. That's that's for each individual type of microcontroller So they support STM 32 is they support the other architectures and there's code like that does I support C for this series So that all goes into chip Then there's common which is Framework code where you have one level above that To do I squared C so if you want to do an I squared C right or read that would be in there Or if you want to do a spy transaction that code would be in there or dealing with commands that the host CPU sends sends to The microcontroller over SPI or I squared C or whatever the bus is that would be in there GPIO code and if you look at core you have like the OS code a scheduler Which is by the way broken if you use GCC newer than 4.8, but I Was willing to even accept that so There in driver you have a bunch of drivers they don't seem to have a real device model So there's room for improvement. I would say but It works In power you have power sequencing code for different SOC classes, so If you look at how you turn on an x86 or if you look at how you turn on a rock chip that always Kind of works the same independent of your board and so so they have their own power sequencing for each Soc class in there include is self-explanatory and in utils you have utilities like easy to all they have a whole bunch of Test and debug tools they made so since since the boards you can use that on are pretty cheap and often have USB You can also use them to do something like USB to SPI or USB to I squared C and they have tools in there that do that so Those would fall in there and there's also an extra directory that I didn't put on the list because it has obscure things in there and In tests you find the unit tests so To configure your firmware image that you're actually building. It's just an include file header that that you just Modify and you say config blah blah. I want kind of like K config minus the menu so Each board under the boards then the board name Let's say Jerry because that's my Chromebook here and then you have a header that configures Which config options you're building into your firmware image and the data structures and That you might also have in header get initialized and start up in the board file So once you're done actually configuring if you build it And that's one of the features that that made it really interesting for me because I had a bad time with firmware updates Last time around with our old product is it already has a firmware update mechanism built in and It's designed in a way that you have a read-only and a read write image In their case, it's mostly for security in my case. It's really because I want to do field upgrades so in the read-only image you always boot into that and then the application processor like your rock chip or your Celeron or what whatever? We'll we'll ask and Be like, okay, what's your hash like like what kind of firmware are you running and then the micro control will reply and say, okay, I'm running this version and The host computer will be all you should be running that version and then overwrite the read write part if it's not what it's supposed to be and then The micro control will jump into the second firmware image and that happens on every boot. So like this. It's really neat because you always have new firmware Synchronized with your kernel. So in my case, I just stick it to get stick it into a flattened FIT file with your boot and just in your boot do that versus what Chromebooks do and They also use a GPIO pin. So To figure out whether you can overwrite that read-only image at the factory They have just a GPIO pin and some Chromebooks That's literally just a screw that you can screw out and then you're in hacker mode or something and And So I duplicated that also for my design because I felt that's pretty neat if you just have a switch And then you can do factory initialization. So you avoid the whole hassle with having to have pre-programmed parts. So manufacturing is happy You just program it there Yeah So what do you get in terms of like as a programmer? Well, Chromium EC has tasks that have individual stacks and They switch between them with interrupts They have priorities mutex as timers events. So it actually feels pretty high-level writing code for it Only caveat is you don't have a heap so you can't malloc and free but well That's okay. I guess So to define the task you have Chromium EC has a bunch of files that get parsed by the build system and turned into something else so in that case you have the EC dot task list which is again board specific and You have a bunch of tasks and it's really easy to define your own tasks. So you have basically the function you want to call and then a bunch of parameters you say, okay, how big is my stack that I want so some some tasks Just link an LED or something so you can have a pretty small stack and others need more stack because they actually do stuff um So we had tasks Then then there's this concept of modules, which is not really the same as like you'd expect from the kernel, but It's basically things that need to keep state and need to get initialized on boot those go into what they call modules and They're self-contained I On start-up they they have an init function that gets called sets up the state machine and You know like I squared C where where it's like you have a bunch of registers You need to write just so the SOC is set up and We'll do I squared C or for SPI or if you have a bunch of ADC channels, which they have if you want to monitor voltages for example Then that gets set up when you have this module in it function Hooks is a pretty neat feature so It's basically a callback so You can say if a certain event happens call this or basically the opposite you can say if a certain event happens Tell me that it happens and call this function. So for SPI that would be if The application processor goes to suspend Stop stop listening to SPI commands because you're going to get garbage if if any so Stuff like oh someone opened the lid So maybe I should actually start turning on the LED to indicate my charge state those kind of things You can stick all that in a hook and basically to declare a hook. It's it's very easy to say Oh, this is my hook name. This is the function and This is the priority so you can also have priorities and say This one has higher priority than another so using hooks and tasks. It's really really convenient to do things like debouncing buttons you just say oh On on that event Call it deferred hook and see if it's still pressed so you don't Since you have like timers and all this going on in the background. It's really a neat High-level way to deal with these kind of issues So the hooks execute in the stack of the calling task, which is recipe for deadlocks, but I Didn't look too much at the design why they made the choice, but it's pointed out throughout the code that this An issue and they all get handled in a special hooks task. So if we go back You see here is a hook task the first one that handles all these things You have a console so it can do stuff like print sort of print F You can show like you can select which level of debug you want by a concept They call channels where you basically define a channel and then you just turn them on or off if you actually want to print them And it's really really useful for board bring up So my hard-ranging here loves me for it because you can just go in log in over you art and and say Turn on that power supply turn on that power supply. Is the power good? Is it not good and you can print out if so so? It's really nice to have You can also add custom commands So if you have say you're getting more confident with your power sequence, then you can just throw it into a function Create a command and just then call it from from the console when all the rest of your debug setup is ready Another neat thing is if you look at the prices of an FTDI chip and you look at the prices of a Cortex M0 If you just want to use the FTDI for USB to you art then If you anyway put the microcontroller you can just use the microcontrollers DMA capability to use one of the microcontrollers you arts to just forward the serial console of your application processor Which is cheaper because you save an entire chip Depending on the microcontroller you pick you might not have enough yards or not enough DMA channels to actually do that well Yeah Console commands how to do it is pretty simple here I Removed most of what actually happens, but just to sort of give a given impression of how you do it So you say declare a console command you say the name of the command behind the scenes obviously there's some magic happening with Macros right Yeah, so let's talk a bit about how to communicate with the application processor again That's like your rock chip your cellar on your zinc whatever It's packet-based so basically you stitch on a header and you have a checksum and data and there's two versions of the protocol Depending on a bus obviously since the buses behave a bit differently ice-cold tea is different from SPI It It kind of the lower levels are a bit different, but in the end the protocol is the same Or it's also pretty awesome. It's like some ecs speak different versions of the protocol and some even both So version two kind of works like that so you have a command which is one byte where Where probably if you think about it for five minutes you already see why that's an issue then you have a command number and You say okay, you have that many parameters You have the parameters and then you have an 8-bit checksum in the end that always adds up to zero if I remember correctly So Then you get back a response which is a result code again a byte and you have a length How long the result will be? you actually have the result and so one of those commands could be read flash at that address and then The length would say oh You read that much and yeah, and then you have a checksum in the end So there's also a version 3 which is oh Can anyone guess what why the version 2 was a bad idea? Going back yes, oh Well, I mean it's after it's after so you can update it right so Nowadays it's More structured someone figured out hey, how cool is that now you have like 16 bits of a number of commands that you can encode that's a whole lot more than 255 So you won't run out of commands that fast On I squared C it's kind of interesting because You kind of have to wrap it with a Mysterious header to make it work So I'm that really worked well in the kernel and it totally didn't work in you boot So I fixed that I mean patches are in the mailing list so that works now Yeah, so it You also have like this this host commands that you actually call with these packets so If you want to declare them and again you have a macro to do that So you come up with a number for the command I haven't figured out what the official process is upstream to come up with those but probably just send a patch and see what happens Then you point it to a function that supposed to get called and you have a version mask So there can be versions of a command so you can for flash info Which tells you about the properties of the flash that's built in It will tell you in the version zero with just the size and the race size and stuff like that and the version one has additional parameters so with that easy version mask in the bottom you Mask you create a mask of supported versions that you can take for that command And then there's another command that allows you to query for each command which versions are supported So that's kind of more food to future proof so I talked a whole lot about firmware and How can you actually use that in your own design? well Looking at your SOC or your processor you have a bunch of requirements There's not a lot, but um, you basically need something to talk to it So that could be SPI which is what most modern Chromebooks seem to do that are not x86 You could use I squared C and you can use LPC and they all have their benefits and drawbacks SPI is basically the benefit is it's really fast The downside is it requires a really decent spike controller because of the way it works so since the protocol the way it works is The microcontroller gets an interrupt and say oh the host wants to talk to me and then It needs to immediately respond So if you build hardware like in an FPGA SPI is really easy if you do it in software not that much because You need to have a real fast response. So the way it works. It will say like yep I got your command everything's fine. It's in progress And then the host computer keeps clocking out cycles until it gets a preamble bite. So The problem with that is that not every spy controller can do that so in my platform, I have a 5-foot at 16 bytes deep and I Need something like 800 bytes or something So it just doesn't work really well and an operating system like Linux where at one point a schedule will be oh Well, you spend like the last couple of hundred milliseconds doing SPI. So no one now it's someone else's turn so in new boot it works perfect for my board in SPI in the kernel I just couldn't make it work because My spy controller sucks. That's what I tell myself Well, if you're a spy controller can do DMA. Yeah, I was about to say like Behind decent spy controller. It's basically like half really deep 5-fold or half DMA because yeah, my SOC just doesn't do that So Which is why I switched over to a I squared C It requires an I squared C controller that can do repeated start which seems to be hey, it's 2017 what right, but Apparently not every spy controller can do that The spy controller on my SOC has a massive bug that you need to work around but Again, that's not a chromium EC's fault. That's just SOC vendors and the drawback. It's it's obviously slower I mean the SPI would run at 5 megahertz versus the I squared C that does 400 kilohertz just The benefit is basically that the The protocol becomes easier because the microcontroller can just say I'm not not ready yet And it's built into the transport basically and then there's LPC We're just kind of for me as an embedded person odd because I don't do a lot of x86 and Like almost no SOC actually does it in the arm space. So Few build x86 boards. That's probably a good choice You need one IRQ probably One or two more pins depending to indicate to the embedded controller whether So the power one is basically like oh now you can turn me off which you could also send as a command But it's easier just with a GPIO and then you have already a kernel driver where you can hook it up and Everything becomes really easy if you have like these three GPIOs Yeah, picking in microcontroller, so it sort of really depends on how much You want your embedded controller to do so Like in here and that one it's a really small microcontroller because it doesn't do so much It does power sequencing it Yeah, so I mean you always need either the SPI and the I squared C I mean it has a really neat feature where you can tunnel I squared C so hardware designer love you for that because sometimes you're short on I squared C buses and pins and Can kind of do sort of port expansion or bridging also. So there's code that does that You probably want to look for PWM if you have LEDs and you need breathing LEDs and If you want to do fan control, it's also a good idea to have PWM You want lots of GPIO and you want DMA channels at least for the communication SPI or I squared C Maybe you want USB I personally really liked the idea of having a bootloader that just works over USB So that my customers which tend to hack our products Pretty badly and like they can't break it because it's built in so you can always recover the firmware. So That that's Kind of it really depends on on how much you want your embedded controller to do Me I'm super cheap obviously because I went with that one with just like 10 bucks and It it gives you USB. It has an STM 32 on it, which is pretty much the same micro controller that you can find in here except for it also does USB Which I like because you can program it over you over USB So what we actually do in manufacturing is we'll just use the USB port and use DFU to flash them The firmware once and then we can use the firmware upgrade mechanism that I talked about before It has DMA. It has I squared C. It has SPI and it's already supported. So there's code for that board already so I figured that's a pretty good target to start with and Another neat hack that you can do with that or hack whatever I mean most of the STMs are actually pin compatible So you can just desoldered that one and take a bigger one and reuse the eval board if you're not a hardware designer like me So that's what I did. I just took the cheapest one I could find for like 10 bucks and soldered a bigger IC that you just get for free as a sample. So Yeah, so then You probably want to look at the pin assignment and pin maxing when you're building a product There's more or less three classes. So you have inputs and outputs which are like strap pins So if you make the same board, but in three different versions and you want to detect that in software Then I like you want to run the same code on three different Small varieties of hardware then use strap pins. I have LEDs and the right protect thing that I talked about So those are just input and outputs You have interrupt sources like external resets or buttons or switches or you know, like the Power-good line of a power supply that you want to be notified if that goes away so Interrupts and then you have alternate alternate functions. So a lot of GPIOs like on on modern microcontrollers can have like so many different functions so you can You sort of before you build the board and it need to sit down and figure out which pin will do what and Going back to the board. I suggested here If anyone tries that and try to use the default configuration for the SPI You need to change a solder bridge on the back of the board to make it work Took me a while to figure that one out So All of those happen in a file. That's called GPIO.ink in your board directory and It's gets transformed by the build. So it's sort of this meta syntax that gets parsed and transformed So have an example again the first one is the right protect they use this naming scheme where underscore L will be low active So it's like right protect. It's pin B4 and it's an input and the battery lead would just pin B11 and it's an output and I wanted to on reset be high and Then the last one is special because it's the interrupt that goes from the embedded controller to your processor and That's pin B9 and you want to be low So the API you then get in the firmware to work with that is Get level and set level which is pretty much Expected I mean it's a GPIO So interrupt sources look like that. So you declare them as GPIO int So in that case you have the chip select for the SPI and then you say oh again pin and you say which edge do I want to get an interrupt for and Then you hook it up to an hand a handler in that case. It's a spy event You have GPIO interrupts for AC present you have flags So you can say oh, I want to enable to pull up or pull down basically my control stuff But wrapped up in a very convenient way, I feel Yeah, the alternate functions it's It's also not where we're well documented. So I figured I make a slide It it depends on the architecture obviously because every microcontroller does the bit differently for the STM one It looks like that. So you have to create a pin mask where the mask is basically all the So the first one is like F zero would be a would be five six Four five six seven and you just order them together and get the mask and the zero is the alternate function for those pins to turn them over to the internal SPI logic and Then you have the same flags that apply to GPIOs where you can say low active high active and so on Another thing that's kind of special a bit is some some code in the core assume certain GPIOs to be there like the right protect So if you don't have it you just declare it unimplemented and it will magically generate the code that fakes it so it works It's really useful. Sometimes also if you just need to try out stuff. You just create fake GPIOs and Yeah, so One case why I ran into is like there's generic LED code that just defines behavior So we have two LEDs here. So there's a power LED and somewhere. There's a charger LED In in my design. I just had one LED, but I didn't want to write new code So did you just say the LED? I'm for the batteries Unimplemented and the code will just run and not do anything for the battery LED Power sequencing which is one of the big reasons why you have the embedded controller in the first place A lot of SOCs need certain sequence They always look sort of like oh turn on the five volt rail wait for X milliseconds then turn on that one Wait for X milliseconds then turn on those two together and wait till the power good comes up and There's a bunch of examples for SOCs that are built into Chromebooks But if you use it for your own project where you have say, I know a Zinc or An iMix 6 then you just have to write your own power sequence, but they come with a pretty neat framework to do that so It Basically what you need to do to support your board sequence as you implement a state machine They use the ACP I naming for power states So like g3 for physically off and s5 and s3 so I just naming you can just look it up But you have power signals which is Core code again where you can assign certain pins as interrupts to be handled by To be handled by the core code as power signals and then if you do that then you can wait for signals So the in P good AP Further up in the code is is you end together a bunch of you or together a bunch of No, you end them together anyway so they basically you can say those following conditions have to be true wait for it and in that case the power good for the AP is the one that says like okay my system on chip is on please don't turn me off and Yeah, they they have a bunch of really Nice functions that make it very easy to write these state machines. So they're all in the power sub directory Interfacing peripherals so yeah, there's there is Reads and rides for I squared C that are already like API wise there. There's an I squared C tunnel which is kind of special. I talked about that before briefly were you could make Peripherals hanging off of the microcontrollers I squared C available to the application Processor by tunneling over the transport you have in between so you can do I squared C over I squared C or you can do I squared C over spy or LPC and That might come in handy In my case I have an e-prom hanging off of the microcontroller That contains serial numbers MAC addresses those kind of things, but since I don't want another useless chip I can also use that to store configuration for the microcontroller because in my case I We built like rack mount devices and Some people put them on their desk So for them the power button should be like I press the power button It turns on the next guy will be like well I have a hundred of them in a rack and I power them over PoE and I Don't want to go to each of them and press a power button for me It's like if I plug it in it needs to turn on so those kind of settings you can just store in the e-prom and Then the then the microcontroller has access to it and the host can also set those settings over the tunnel And there's locking in between to make sure it doesn't go wrong There's a SPI API for being an SPI master so Some of the embedded controllers use SPI flash But you can also use it to talk to SPI devices obviously So there's a transaction call where you say data Transmit length and a pointer to receive data and receive length. So it's pretty high level Can do the same in an asynchronous way, but DMA is required Yeah so I Mean personally, I really really like working with upstream stuff I don't want to bother with stupid like five-year-old vendor kernels that are broken all the time. So Chromebooks use heavily patched really old kernels, but From what I've seen in the commit logs as they spend amazing amount of time and money to backport things for a product like that that costs 80 bucks and Since since by design, they're basically Linux laptops. They usually work really well and So the integration also in the kernel is is already there and just works so For me often when I needed support for something in you boot I can can just look at the kernel code and Hack it up to work in you boot Most of the actual Chromebooks use their own bootloader instead of you boot So the actual you boot code gets less flight time. Let's say there's Little motivation to actually run you boot on a real Chromebook So when I started out Trying stuff it was just terribly broken and like there's no way this ever ran on any hardware because it just didn't work but So so I talked to Simon who's I'm working on that in your boot and Send a bunch of patches and now now it's working very well The one thing that's missing is this software is synchronization that I talked about with the firmware on boot up I have code that does that But I'm still sort of trying to figure out how to do it in a way so it fits into the you boot device model and and works for Chromebooks and non Chromebooks because the Chromebooks have a certain way of describing their firmware images and It's not what I want for my platform. So but it needs to work for both. So I'm still working on that I didn't look at adding verified boots sort of Beyond what my customers care about so So Talking about your boot talking about kernel you always end up with a device tree. So instantiation is pretty obvious For guys court see you give it an address. You say this is a Google I squared C Embedded controller you need an interrupt an interrupt parent and it's a wake-up source if you have a tunnel then you have If you tunnel I squared C then you'd have a bunch of sub nodes still but it's already well documented For SPI it looks pretty much the same so I give it a chip select in that case the first one and You say this is an SPI version of the chromium AC and Your IQ level is in that case low and it's all documented in the criminal What do you get in Linux to talk to it? Well, it's not really all that exciting. I mean it's just a janitor basically right so it's it's a Mfd multi-function device and the sub devices that are right there in mainline at the moment I think is the ice courtesy tunnel which is just represented as a normal ice court C bus in Linux so Linux wouldn't even know it's tunneling through the embedded controller over SPI to talk courtesy with it PWM channels, I think is pretty new. I think 4.9 maybe no, I know something like that Battery there's drivers for the light bar a lot of that We'll just show up in ccfs under Cis platform Chrome OS Cross EC and then something There's a character device that allows you to do eye octals So there's EC tool that I'll talk about later a bit which is for debug really useful because you can Rapidly prototype commands before you start and sit down and write kernel code which Arguably takes longer because you get a reboot and Most of the code in the kernel is actually in drivers Mfd cross EC ice court C spy and Drivers platform Chrome where all the protocol stuff which is the same for all lives Yeah, in Cis if acid basically looks like that the Cis class Chrome OS and then you have power and you have flash info which you can just cat and it will tell you this is a I Don't know microchip MCU it has that that much flash the race size at that size and Then there's EC tool There's a lot of functionality already there so you can like query GPO states you can Create firmware versions you can reboot embedded controllers so you can do it like the whole jump between firmware A and B Or read only and read write can get flash info you can read and write the firmware So obviously you should make sure that only road can do that You can send commands and in general it's really really useful for development because as you keep adding your own commands As I keep adding your functionality you can I mean you write a bit of C code and you don't have to write kernel or your boot code In your boot, it's also not very exciting what you get It's just a miscellaneous device. It's integrated in the device model already You also get the ice court C tunnel so it again just shows up as a separate bus and you can read and write the firmware You can get flash info again You can reboot the EC so You can also jump back and forth In your boots so if you want to do like a super trivial a Super ghetto your boot script software sync you can do that also by just reading back the firmware on every boot and Just doing doing a mem compare with what you have or what it should be and if not write it So the functionality is all there. It just needs to be tied up in Yeah The code codes and the director is there So, um, I know I probably I'm super fast Now let's talk a bit about community because let's force them and So it seems to me from my observation no one ever talked about this So I must be one of the few people that do that outside of Google I mean, it's very not clear what's going to happen next because It's Google internal development every now and then there's like someone pushes stuff to get and say oh something happened and That being said there they're very receptive to patches like I was amazed you send like a day before Christmas You send the patch using Garrett, which is annoying, but Anyway, someone reviews it within like three days. So That was actually quite nice and Other than the kernel you don't get yelled at for misspelling your commit messages. So There's a mailing list I think it's like I don't have a link, but it's very low traffic. So I Would say there is no real community, but I'm starting it Yeah, I think that's all I had So I think we still have a bit of time for questions Yes, I'm just gonna repeat it so no one needs to run. Yes So the question was basically whether the hooks being called in the Callbacks being called in this stack and I have to in the same stack as the caller Yeah, anyway short answer is I don't have an answer for that, but Yes, it's probably bad which is Yes, probably I mean It's the tasks are very Very simple C code so Personally I've tried to break it and I couldn't get to that point so I Think it's pretty annoying if you actually run into it and it happens and you don't know why which is why I put it on the slides but So the question was whether the code generation that I talked about is macro metaprogramming or whether Or whether there's a code generator. I think it's literally just macros which I guess is fine for firmware