Recompile option.c usb serial driver

Hi all,

i am trying to edit option.c and then replace the existing option.ko file…

I used

apt-get install linux-source (to get the option.c file)

then edited the option.c file to add my code

But then whats the next step ?

To start with, only use the kernel source from NVIDIA. If you check “head -n 1 /etc/nv_tegra_release” you can find your L4T release (which is Ubuntu plus NVIDIA drivers). You can then go here and find the URL for your exact release:

You can edit in that. Then you must configure the source to be an exact match to the existing source, although you’ll need a new CONFIG_LOCALVERSION.

If you run the command “uname -r” you’ll find that the prefix is the release version of the kernel source, and the suffix is from setting the CONFIG_LOCALVERSION (default is “-tegra”, but you’ll probably change this to something like “-option” since you don’t want to mix the two).

Note that by default the “tegra_defconfig” is what a Jetson’s kernel would ship with, but this does not set CONFIG_LOCALVERSION. Also, “/proc/config.gz” reflect’s the kernel’s running config (though it does not show CONFIG_LOCALVERSION).

If your kernel feature which uses that driver is already enabled, then you just build and install preferably using an alternate kernel name so you can keep the working one in place and use serial console to select an alternate entry with that kernel; failure would mean you could use the original kernel image boot entry (see “/boot/extlinux/extlinux.conf”).

If your kernel feature using that file is not enabled, then you’d use a config editor to enable this (config editors understand dependencies). Examples: menuconfig or nconfig (I prefer the latter due to symbol search).

For any details you might need regarding this you’d have to ask and specifically mention if it is cross compiled or natively compiled. The documentation with a given L4T release states how to cross compile. Installation would likely differ from what the docs say.

Hi @linuxdev

To be specific

I am trying to follow this and add the simcom driver code into the option.ko

My problem is that I cannot seem to compile it, their make script isn’t working on the orin nano dev kit

I am initially trying to do this on the devKit itself

I have not read that full document. I will say though that anything which produces a file “/dev/ttyUSB#” is using the FTDI serial UART driver, and no modification is needed for that. I don’t know if the kernel source file specific to this case has the #define lines, but if they do not, then you would add them to that file in the source which is for your specific L4T release.

Building would still require first setting up configuration correctly. Then that paper moves on to building just that module. You should know that if configuration does not initially match, there is a good chance that the module won’t load after you build it. If the edits have an effect only on that module, then you should be able to build the module mentioned using their commands. I would, however, personally not choose to build within the source tree. I’d first, within that source, run “sudo make mrproper” to full clean it up. From that point forward all of my build commands would name an empty directory for all work via the “O=/some/where” option. Example:

# $TOP refers to the root of the kernel source code.
cd $TOP
sudo make mrproper
# Now we operate only as a regular user.
mkdir ~/kernel
export TEGRA_KERNEL_OUT=~/kernel
make O=$TEGRA_KERNEL_OUT tegra_defconfig
# Edit your "~/kernel/.config" such that:
# Note that in the above it assumes you do not modify the base kernel,
# and that your edits have an effect ONLY on that module. Otherwise
# you should expect to rebuild and install EVERYTHING, including kernel
# and ALL modules with an alternate CONFIG_LOCALVERSION.

# This is not always needed, but I suggest you propagate the configuration:
make O=$TEGRA_KERNEL_OUT modules_prepare

The above is setting up for the build line in the PDF at the URL you gave. The difference is that now you would also use “O=$TEGRA_KERNEL_OUT”, e.g.:

sudo make O=$TEGRA_KERNEL_OUT -C /lib/modules/`uname -r`/build M=`pwd`/drivers/usb/serial
obj-m=option.o modules

Your file to copy would be in a subdirectory of “$TEGRA_KERNEL_OUT”.

I am a fan of building the full kernel Image target once with a given configuration (after tegra_defconfig and setting up CONFIG_LOCALVERSION) just as an acid test. Then I’d build all modules once to see if it is valid. Not necessary, but is a good test.

Note that if you build only modules, and not Image, then you need to build target modules_prepare, or the configuration won’t be correct for building modules (building Image takes care of that for you).

For the individual configurations listed in that paper, I’d recommend using nconfig (it’s almost exactly the same as menuconfig, but it has a symbol search, so you could search for CONFIG_PPP more easily). Example, before modules_prepare:
make O=$TEGRA_KERNEL_OUT nconfig

I almost forgot: This is for native compile. Don’t forget the options for cross compile if not natively compiling.

1 Like

Thank you so much @linuxdev

That’s a great help…

The issue is my lack of experience with kernel modification in general (I am a hardware engineer)

What is interesting, is the simcom chip does enumerate as


So maybe I have been looking at the wrong linux source code …

I initially flashed the dev kit with the sdk and it created a “headers” folder but that folder didn’t have the option.c file , I am not at my pc but I am pretty sure it only have option.ko

So I searched google and found the command to download the source and ran it (but I am not confident this downloaded the right source)

I ran

apt-get install linux-source

And then looked in that to inspect to see if option.c had the lines of code that simcom specify … which it didn’t

Was this correct so far? Or should I already have the source code on my machine (the host or the target)

When native compiling , which folder of the file system should I be running the build / make commands from ?

FYI, every file in “/dev” is a “pseudo” file, and thus it is not on disk and is a driver providing a “file like” interface. The FTDI serial USB UARTs produce files of that naming convention, and thus the driver is already present.

On top of this, USB is a “hot plug” arrangement. Anything plug-n-play has an ability to announce a description of itself to another layer, and then drivers can take ownership if they have the capability (so PCI and USB and HDMI and DisplayPort are typical plug-n-play devices with that theme). In terms of the USB devices you see, there is one other thing to consider for vendor-specific hardware which otherwise uses only standard drivers: That would be the udev system.

As a USB device plugs in, it is udev which takes the USB information and announces it to the drivers. It is quite common that a vendor with some major product using a common chipset for USB communications will set up udev to customize something. An example would be that udev might trigger upon seeing a certain serial number or other vendor specific ID and rename the device, e.g., it could rename ttyUSB# to be ttyMyBrand#; then the user space software would know that this is not “just an FTDI generic UART”, and that it applies to their software. I have no idea if there has been any udev required trigger to rename the files or in some other way identify the device for other software.

Incidentally, it also is not unusual for udev to produce multiple symbolic links to the standard file name. A good example which is based on function rather than brand is to look at “/dev/input/*”. It is a more or less generic scheme to present a “sort of” object oriented access to HID (Human Interface Devices) devices (e.g., keyboard, mouse).

So a question to ask is “does this work on some other Linux PC type computer?”. If it does, then perhaps there is a udev script installed and plugin triggers that. If not, then what instructions does the manufacturer suggest? Other than that PDF manual, are there “driver” downloads or other installation downloads?

Almost forgot: Native compile means you compile from the $TOP. The “O=...” just tells the build about where to put temporary output (and there is a lot of it for a kernel, several GB). And you can’t use the apt-get kernel source unless NVIDIA has set that up.

One more note: Can you describe if another Linux PC works with this? If it does, what do you see for “lsmod” when it is running?

FYI, I might not be able to respond part of the upcoming week.

1 Like

Hi @linuxdev

Thanks for the info, I had reasonable understanding of the dev/try stuff

Is $TOP pre defined the. ? I hadn’t realised that …

Also, I have only tried on the virtual machine I am running (Ubuntu 18.04)

Actually what has started me to go down the route of looking into this is that it gave me the following error a few minutes after plugging the simcom module into the usb port of the Orin nano … nearly every time …

Which is what made me think there is a driver issue… do you recognise any of that? That’s from the debug TTL console on the Orin nano dev kit

when plugging in the simcom module to the orin nano, running lsmod it shows it will load

usbserial : usb_wwan, option

When the module is plugged in, but it always crashes with above log after a few mins

The screenshot is a USB error, but I couldn’t say where. It’d be interesting to see on a protocol analyzer, as it seems to not be a signal quality issue. It is possible software or firmware changes could fix such an issue, but I have no idea of where to change something. The changes you are looking at might deal correctly with this if you use the changes on the NVIDIA kernel source after correct configuration.

Regarding the usb_wwan,option, what do you see from:
zcat /proc/config.gz | egrep 'USB_SERIAL_(WWAN|OPTION)'

Note that config.gz is a compressed pseudo file which the kernel publishes to show its compile time options.


i get


I am trying to run make mrpoper as per your guidance but guess i am not doing it from the right folder as it wont run

That means that your Jetson has the features that your “working” Linux system has. If you were to match the existing Jetson’s kernel config prior to editing the USB source, then if that edit worked on the other Linux system, it should also work on the Jetson.

If your edits touch only those modules, then you can avoid building and installing the integrated Image kernel file, and also reuse the existing modules (other than the modules you are editing). Should you touch anything else in the source you’ll probably need to rebuild everything using a new CONFIG_LOCALVERSION to avoid touching the old Image and modules.

I am assuming your original kernel is still in place, and that it is this original kernel (and kernel config) which is showing:


This seems likely to be correct:

  • From the $TOP of kernel source “sudo make mrproper”.
  • Configure the NVIDIA source to match the original Jetson system since it already has those options.
  • Configure CONFIG_LOCALVERSION as “-tegra”.
  • Edit option.c.
  • Propagate configuration via “make O=/where/ever/you/put/temp/output modules_prepare”.
  • make -j 6 O=/where/ever/you/put/temp/output modules


  • I assume all configuration and output goes to a clean empty location, “/where/ever/you/put/temp/output” (make this wherever you wish; documents normally name that via the $TEGRA_KERNEL_OUT environment variable).
  • I am suggesting to build all modules. I don’t know what else might be affected by the edits. See if it works with all modules built.
  • The “-j #” argument is for how many CPU cores to use. It is optional and I’m assuming 6 cores to use during build.
  • The “make mrproper” does not use the “O=/some/where” temporary output location. It could, but that is not what we are doing. An empty “O=/some/where” directory is already clean. It is the original source code we need to guarantee is pristine, thus leaving out “O=...” from “mrproper”.
  • If “make mrproper” ever fails, this is quite unusual, and you should include the output of the failure (if and only if it fails):
    sudo make mrproper 2>&1 | tee 'mrproper_fail.txt

I might not be available for reply for a few days after this morning.

i feel a little lost … :)

I cannot find the kernel_src.tbz2 after starting over again and downloading from, here…

kernel_src.tbz2 is not directly listed. It is a “sub package” of “Driver Package (BSP) Sources”. You download that. Then, you unpack and kernel_src.tbz2 is a package within that package. I don’t know what exact release you are using, but due to being Orin Nano, I assume it is L4T R35.3.1, located here:

If that is correct, then the “whole” source is downloaded here:
(package public_soruces.tbz2)

The package “within” this is “Linux_for_Tegra/source/public/kernel_src.tbz2”. To extract that without extracting everything else (it is a very large archive file):

tar -xjf public_sources.tbz2 Linux_for_Tegra/source/public/kernel_src.tbz2


cd Linux_for_Tegra/source/public
tar xjf kernel_src.tbz2
cd kernel/kernel-5.10
export TOP=`pwd`
echo $TOP

I probably won’t be able to reply for a few days.

Hi @linuxdev thanks for the help

I downloaded the kernel source and extracted

Here are my steps

  1. Get kernel source from Jetson Linux Archive | NVIDIA Developer

Make sure you use the same kernel version as is on the Orin Nano

You need to download BSP Sources

  1. Untar the BSP file:

tar -xjf public_sources.tbz2

  1. Untar the kernel :

cd Linux_for_Tegra/source/public

tar –xjf kernel_src.tbz2

  1. Set KERNEL_OUT_DIR variable


  1. Run (it will use the KERNEL_OUT_DIR location as the kernel location as follows)


Kernel will be found in the above folder.

Modifying drivers

USB Serial:

Locate option.c in drivers/usb/serial/option.c

Gedit and perform edits as per simcom guidance

Add definition of option_blacklist_info above the struct call as follows

struct option_blacklist_info {
/* bitfield of interface numbers for OPTION_BLACKLIST_SENDSETUP /
const unsigned long sendsetup;
bitfield of interface numbers for OPTION_BLACKLIST_RESERVED_IF */
const unsigned long reserved;

Run ./

then replace the exiting option.ko on the jets with the newly compiled option.ko.

but after this the LTE module isn’t detected properly at all, you only see ttyUSB0

I did perform depmod command too after swapping the .ko files.

I’m not where I can research this right now, but what do you see for “uname -r” once this kernel is in place?


Well actually I only replaced the option.ko module

Is that a stupid thing to do?

Should I replace the whole kernel folder ?

If building as a module is supported (not all features can be a module, although many can), and if you started with an exactly matching config, including CONFIG_LOCALVERSION, then just replacing option.ko would be recommended. However, you’ve changed the source code.

When changing this, if it did not change the interface specification, then it might be ok. I can’t say. When installed, if you can “insmod” the module, then you are probably ok.

Did you modify code in just that module? If so, then don’t worry about the other modules. I recommend though to use a new CONFIG_LOCALVERSION and thus get an entire set of new modules and a new Image for a second boot spec in extlinux.conf. Not necessary, but it is safer.

Did you start with an exact matching specification to the running kernel? Was CONFIG_LOCALVERSION the same “-tegra”? If so, and if you can insmod, then if it works you should be ok (it might automatically load, so check after boot with lsmod).

1 Like

hi @linuxdev

thanks again.

I will check insmod

I only modify that module, nothing else, everything else is the same in these tests…

I didn’t explicitly set CONFIG_LOCALVERSION during my steps, but i did follow nVidia guance and used their script

I am not quite sure where to put the second set of modules, so i literally just copy the entire kernel output from the process into /lib/modules?

and instead of 5.10.104-tegra the folder will be names 5.10.104-test (if i set CONFIG_LOCALVERSION to “test” ?

If you did not set CONFIG_LOCALVERSION, and you are using a kernel which had this set when created, then likely this will cause failure.

The “uname -r” is determined by the kernel itself, the Image file. Modules are searched for by that kernel at:
/lib/modules/$(uname -r)/kernel

You place the modules based on the kernel’s uname -r, and that in turn is also a mirror of the subdirectory in the kernel source. So if you are using a kernel producing “5.10.104-test”, then the base of kernel modules is at:

If you created a kernel module, and in that source it is found as “drivers/net/foobar.ko”, then you would add this at:

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.