Install cups driver for Brother printer

My network printer, a Brother hll2365dw, responds to pinging its IP, but CUPS can’t see it. This may well be because the driver I installed is for a later model:
nix-env -iA nixos.hll2390dw-cups
the closest fit I could see.

For good measure, I restarted CUPS:
sudo systemctl restart cups.service

I’ve just arrived from Slackware, which is probably about as far removed as could be from the NixOS philosophy. I got the printer to work there using a script from the Brother webside, for hll2360DW rather than the actual model number ~65. But I hope to work out how to solve problems like this “the NixOS way”.

Add to /etc/nixos/configuration.nix:

{
  ...
  services.printing.enable = true;
  services.printing.drivers = [ pkgs.brlaser ];
  ...
}

read more here:
https://nixos.wiki/wiki/Printing

Thanks, but that doesn’t seem to have done it. Could it be something to do with the driver I installed using nix-env?

I already had
services.printing.enable = true;
as per the installation doc (extremely well-written, by the way)
but not the second line.

please elaborate

If you can at least see the Brother drivers in the “add printer” wizard of the CUPS web admin, then the NixOS-specific part was done successfully.

No, CUPS shows nothing at all, either under Add printer or Find printer.

I have since dug a tiny bit deeper and looked at the brlaser package, which states that it works for hll2360dw. My printer is hll2365dw, but on my Slackware system, it works with the driver for 60 - and that’s the closest the vendor provides. So it ought to work, in theory.

One thing that occurs to me is is there a CUPS/printing group my user should be added to? At the moment, I have just wheel and networkmanager.

EDIT
For good measure, here is my configuration.nix (sorry it’s a bit of a dog’s breakfast - I had some other problems along the way):

[mimosa@nixos:~]$ cat /etc/nixos/configuration.nix

{ config, pkgs, ... }:

{
  imports =
    [ # Include the results of the hardware scan.
      ./hardware-configuration.nix
    ];

  # Use the systemd-boot EFI boot loader.
  boot.loader.systemd-boot.enable = true;
  boot.loader.efi.canTouchEfiVariables = true;
#  boot.loader.generationsDir.enable = true;
# boot.loader.generationsDir.copyKernels = true;

  # networking.hostName = "nixos"; # Define your hostname.
# networking.wireless.enable = true;  # Enables wireless support via wpa_supplicant.

networking = {
	  hostName = "nixos"; # Define your hostname.
#		    interfaceMonitor.enable = false; # Disable in favour of wicd
		      wireless.enable = false;  # Disable in favour of wicd

#			      };
#		      };
		        useDHCP = false;  # Disable in favour of wicd
                        interfaces.wlan0.useDHCP = true;
#			  wicd.enable = true;
			  networkmanager.enable =true;
};

  
  # List packages installed in system profile. To search, run:
  # $ nix search wget
   environment.systemPackages = with pkgs; [
     wget vim pciutils wirelesstools firmwareLinuxNonfree iw
   ];

 
  # Enable CUPS to print documents.
  services.printing.enable = true;
  services.printing.drivers = [ pkgs.brlaser ];

  # Enable sound.
  sound.enable = true;
  hardware.pulseaudio.enable = true;

  # Enable the X11 windowing system.
  services.xserver.enable = true;
  services.xserver.layout = "us";
  services.xserver.xkbVariant = "dvp";
  # services.xserver.xkbOptions = "eurosign:e";

  # Enable the KDE Desktop Environment.
  services.xserver.displayManager.sddm.enable = true;
  services.xserver.desktopManager.plasma5.enable = true;

  # Define a user account. Don't forget to set a password with ‘passwd’.
  users.users.mimosa = {
  isNormalUser = true;
  extraGroups = [ "wheel" "networkmanager" ]; # Enable ‘sudo’ for the user.
  };

  system.stateVersion = "19.09"; # Did you read the commente?
  
    # Enable virtualbox.
    virtualisation.virtualbox.host.enable = true;
    #
    #     # Enable the Oracle Extension Pack.
    virtualisation.virtualbox.host.enableExtensionPack = true;

}

Did you ever figure this out? I have a similar (same?) printer, and my printer config looks like this:

    printing.enable = true;
    printing.drivers = [
      pkgs.brlaser
      #pkgs.hll2390dw-cups
      (pkgs.callPackage ../../printers/hll2395dw-cups.nix {})
    ];

My custom printer driver package (../../printers/hll2395dw-cups.nix) looks like this:

{ stdenv, fetchurl, makeWrapper
, cups
, dpkg
, a2ps, ghostscript, gnugrep, gnused, coreutils, file, perl, which
}:

stdenv.mkDerivation rec {
  name = "hll2395dw-cups-${version}";
  version = "4.0.0-1";

  src = fetchurl {
    url = "https://download.brother.com/welcome/dlf103564/hll2395dwpdrv-4.0.0-1.i386.deb";
    sha256 = "18zy039liqrh0wykays0b4np9mjfy233s8il0d9njbiwl7nnz9p2";
  };

  nativeBuildInputs = [ makeWrapper ];
  buildInputs = [ cups ghostscript dpkg a2ps ];

  unpackPhase = ":";

  installPhase = ''
    dpkg-deb -x $src $out

    substituteInPlace $out/opt/brother/Printers/HLL2395DW/cupswrapper/brother-HLL2395DW-cups-en.ppd \
      --replace '"Brother HLL2395DW' '"Brother HLL2395DW (modified)'

    substituteInPlace $out/opt/brother/Printers/HLL2395DW/lpd/lpdfilter \
      --replace /opt "$out/opt" \
      --replace /usr/bin/perl ${perl}/bin/perl \
      --replace "BR_PRT_PATH =~" "BR_PRT_PATH = \"$out\"; #" \
      --replace "PRINTER =~" "PRINTER = \"HLL2395DW\"; #"

    # FIXME : Allow i686 and armv7l variations to be setup instead.
    _PLAT=x86_64
    patchelf --set-interpreter $(cat $NIX_CC/nix-support/dynamic-linker) \
      $out/opt/brother/Printers/HLL2395DW/lpd/$_PLAT/brprintconflsr3
    patchelf --set-interpreter $(cat $NIX_CC/nix-support/dynamic-linker) \
      $out/opt/brother/Printers/HLL2395DW/lpd/$_PLAT/rawtobr3
    ln -s $out/opt/brother/Printers/HLL2395DW/lpd/$_PLAT/brprintconflsr3 $out/opt/brother/Printers/HLL2395DW/lpd/brprintconflsr3
    ln -s $out/opt/brother/Printers/HLL2395DW/lpd/$_PLAT/rawtobr3 $out/opt/brother/Printers/HLL2395DW/lpd/rawtobr3

    for f in \
      $out/opt/brother/Printers/HLL2395DW/cupswrapper/lpdwrapper \
      $out/opt/brother/Printers/HLL2395DW/cupswrapper/paperconfigml2 \
    ; do
      #substituteInPlace $f \
      wrapProgram $f \
        --prefix PATH : ${stdenv.lib.makeBinPath [
          coreutils ghostscript gnugrep gnused
        ]}
    done

    # Hack suggested by samueldr.
    sed -i"" "s;A4;Letter;g" $out/opt/brother/Printers/HLL2395DW/inf/brHLL2395DWrc

    mkdir -p $out/lib/cups/filter/
    ln -s $out/opt/brother/Printers/HLL2395DW/lpd/lpdfilter $out/lib/cups/filter/brother_lpdwrapper_HLL2390DW

    mkdir -p $out/share/cups/model
    ln -s $out/opt/brother/Printers/HLL2395DW/cupswrapper/brother-HLL2395DW-cups-en.ppd $out/share/cups/model/

    wrapProgram $out/opt/brother/Printers/HLL2395DW/lpd/lpdfilter \
      --prefix PATH ":" ${ stdenv.lib.makeBinPath [ ghostscript a2ps file gnused gnugrep coreutils which ] }
    '';

  meta = with stdenv.lib; {
    homepage = "https://www.brother.com/";
    description = "Brother HL-L2395DW combined print driver";
    license = licenses.unfree;
    platforms = [
      "x86_64-linux"
    ];
  };
}

This was a hack to make the printer not always try to print to A4 paper (or something like that–I forgot the details but if you search my post history you can find the place where I was getting help for this).

I suspect this won’t really help you with your problem, but I hope it does!

roni

1 Like

@roni Thanks very much for your reply.

What probably makes sense in my situation (no package for my model) is to adapt your build script for my model. But I’m quickly at sea. Here’s the relevant page from the manufacturer’s site (mainly note to self):
https://support.brother.com/g/b/downloadlist.aspx?c=us_ot&lang=en&prod=hll2360dw_us&os=128#SelectLanguageType-10032_0_1
Here’s the modified derivation (with the paper size modification commented out, since A4 is all there is in these parts) with a direct link and checksum for the “LPR printer driver (deb package)”, which seems to be the right one:

{ stdenv, fetchurl, makeWrapper
, cups
, dpkg
, a2ps, ghostscript, gnugrep, gnused, coreutils, file, perl, which
}:

stdenv.mkDerivation rec {
  name = "hll2360dw-cups-${version}";
  version = "3.2.0-1";

  src = fetchurl {
    url = "https://download.brother.com/welcome/dlf101916/hll2360dlpr-3.2.0-1.i386.deb";
    sha256 = "a468b1d53114b1f6580d41ed2aa4b1be72dad4eadce4f017d37a4e36a9ecaf38";
  };

  nativeBuildInputs = [ makeWrapper ];
  buildInputs = [ cups ghostscript dpkg a2ps ];

  unpackPhase = ":";

  installPhase = ''
    dpkg-deb -x $src $out

    substituteInPlace $out/opt/brother/Printers/HLL2360DW/cupswrapper/brother-HLL2360DW-cups-en.ppd \
      --replace '"Brother HLL2360DW' '"Brother HLL2360DW (modified)'

    substituteInPlace $out/opt/brother/Printers/HLL2360DW/lpd/lpdfilter \
      --replace /opt "$out/opt" \
      --replace /usr/bin/perl ${perl}/bin/perl \
      --replace "BR_PRT_PATH =~" "BR_PRT_PATH = \"$out\"; #" \
      --replace "PRINTER =~" "PRINTER = \"HLL2360DW\"; #"

    # FIXME : Allow i686 and armv7l variations to be setup instead.
    _PLAT=x86_64
    patchelf --set-interpreter $(cat $NIX_CC/nix-support/dynamic-linker) \
      $out/opt/brother/Printers/HLL2360DW/lpd/$_PLAT/brprintconflsr3
    patchelf --set-interpreter $(cat $NIX_CC/nix-support/dynamic-linker) \
      $out/opt/brother/Printers/HLL2360DW/lpd/$_PLAT/rawtobr3
    ln -s $out/opt/brother/Printers/HLL2360DW/lpd/$_PLAT/brprintconflsr3 $out/opt/brother/Printers/HLL2360DW/lpd/brprintconflsr3
    ln -s $out/opt/brother/Printers/HLL2360DW/lpd/$_PLAT/rawtobr3 $out/opt/brother/Printers/HLL2360DW/lpd/rawtobr3

    for f in \
      $out/opt/brother/Printers/HLL2360DW/cupswrapper/lpdwrapper \
      $out/opt/brother/Printers/HLL2360DW/cupswrapper/paperconfigml2 \
    ; do
      #substituteInPlace $f \
      wrapProgram $f \
        --prefix PATH : ${stdenv.lib.makeBinPath [
          coreutils ghostscript gnugrep gnused
        ]}
    done

    # Hack suggested by samueldr.
 #   sed -i"" "s;A4;Letter;g" $out/opt/brother/Printers/HLL2360DW/inf/brHLL2360DWrc

    mkdir -p $out/lib/cups/filter/
    ln -s $out/opt/brother/Printers/HLL2360DW/lpd/lpdfilter $out/lib/cups/filter/brother_lpdwrapper_HLL2390DW

    mkdir -p $out/share/cups/model
    ln -s $out/opt/brother/Printers/HLL2360DW/cupswrapper/brother-HLL2360DW-cups-en.ppd $out/share/cups/model/

    wrapProgram $out/opt/brother/Printers/HLL2360DW/lpd/lpdfilter \
      --prefix PATH ":" ${ stdenv.lib.makeBinPath [ ghostscript a2ps file gnused gnugrep coreutils which ] }
    '';

  meta = with stdenv.lib; {
    homepage = "https://www.brother.com/";
    description = "Brother HL-L2360DW combined print driver";
    license = licenses.unfree;
    platforms = [
      "x86_64-linux"
    ];
  };
}

I’m stumped by the relative path to the derivation in your configuration.nix (…/…/printers/ …). Where should it go? Presumably the only way to test it is by rebuilding the configuration, with everything in place. Fingers crossed the build works without adjustments.

Hi! I should maybe have been more explicit about the way my config files are set up.

Check out my nixos-config repository (and excuse its fledgling nature) and that should clear things up: GitHub - waxlamp/nixos-config: NixOS configuration files. If not, let me know and I’ll do my best to explain (I have been using NixOS for several years now, but still feel like a beginner).

Good luck!

roni

Thanks - I see you can choose whatever path (and the derivation is indeed then called). I’m now stuck again because the needed files for my printer are distributed over two downloads (whereas for the …95 printer, it’s just one item). After some time searching through the documentation, I can’t work out how to specify more than one source file. So I’ve got

src = fetchurl {
    url = "https://download.brother.com/welcome/dlf101916/hll2360dlpr-3.2.0-1.i386.deb";
    sha256 = "a468b1d53114b1f6580d41ed2aa4b1be72dad4eadce4f017d37a4e36a9ecaf38";
    url = "https://download.brother.com/welcome/dlf101917/hll2360dcupswrapper-3.2.0-1.i386.deb";
    sha256 = "a9eeae8d71fc32d209833d4b1bc8ecf4d530ab7b46301d510018f18554191fd0";
  };

which of course doesn’t work. I see that $src is referenced further down the script, which makes me suspect you could just use src0, src1 … srcn, i.e. “src” is just an arbitrary label rather than a reserved name; I thought I’d ask though before trying any more such random experiments. It would make more sense on the face of it for sources to be presented in a list or array.

I see that $src is referenced further down the script, which makes me suspect you could just use src0, src1 … srcn, i.e. “src” is just an arbitrary label rather than a reserved name

In this particular case, I think your idea of creating extra src<N> values will work. In general, I think the src value has special meaning to mkDerivation, since it references the various build phases, one of which is used to unpack the source tarball referenced by src. You can see in this example it is set to :, I think because the src in this case is a .deb file, rather than a tarball that needs to be unrolled.

I guess I’m saying to go ahead and try your approach out; it can’t hurt! And in the meantime, perhaps someone who understands these things better can come by to confirm or disconfirm what I said above :slight_smile:

roni

this is not needed. I’ve made brlaser working without doing this

Partial success - the derivation builds and CUPS allows me to install the printer, however, the test page tells me that “If you can read this, you are using the wrong driver for your printer”.

This is probably now a packaging problem rather than a NixOS problem, and lies in the nitty-gritty of the files that make up the “driver”. So to sum up, having two $src variables or labels works; I’ve edited the paths and filenames to match the driver for this model, but may have slipped up. I can try and dig deeper, but can anyone spot any obvious mistakes?

Here is the new derivation:

{ stdenv, fetchurl, makeWrapper
, cups
, dpkg
, a2ps, ghostscript, gnugrep, gnused, coreutils, file, perl, which
}:

stdenv.mkDerivation rec {
  name = "hll2360dw-cups-${version}";
  version = "3.2.0-1";

  src_lpr = fetchurl {
    url = "https://download.brother.com/welcome/dlf101916/hll2360dlpr-3.2.0-1.i386.deb";
    sha256 = "a468b1d53114b1f6580d41ed2aa4b1be72dad4eadce4f017d37a4e36a9ecaf38";
  };
  src_cupswrapper = fetchurl {
    url = "https://download.brother.com/welcome/dlf101917/hll2360dcupswrapper-3.2.0-1.i386.deb";
    sha256 = "a9eeae8d71fc32d209833d4b1bc8ecf4d530ab7b46301d510018f18554191fd0";
  };
  nativeBuildInputs = [ makeWrapper ];
  buildInputs = [ cups ghostscript dpkg a2ps ];

  unpackPhase = ":";

  installPhase = ''
    dpkg-deb -x $src_lpr $out
    dpkg-deb -x $src_cupswrapper $out

    substituteInPlace $out/opt/brother/Printers/HLL2360D/lpd/filter_HLL2360D \
      --replace /opt "$out/opt" \
      --replace /usr/bin/perl ${perl}/bin/perl \
      --replace "BR_PRT_PATH =~" "BR_PRT_PATH = \"$out\"; #" \
      --replace "PRINTER =~" "PRINTER = \"HLL2360D\"; #"

    for f in \
      $out/opt/brother/Printers/HLL2360D/cupswrapper/brother_lpdwrapper_HLL2360D \
      $out/opt/brother/Printers/HLL2360D/cupswrapper/paperconfigml1 \
    ; do
      #substituteInPlace $f \
      wrapProgram $f \
        --prefix PATH : ${stdenv.lib.makeBinPath [
          coreutils ghostscript gnugrep gnused
        ]}
    done

    mkdir -p $out/lib/cups/filter/
    ln -s $out/opt/brother/Printers/HLL2360D/lpd/filter_HLL2360D $out/lib/cups/filter/brother_lpdwrapper_HLL2360D

    mkdir -p $out/share/cups/model
    ln -s $out/opt/brother/Printers/HLL2360D/cupswrapper/brother-HLL2360D-cups-en.ppd $out/share/cups/model/

    wrapProgram $out/opt/brother/Printers/HLL2360D/lpd/filter_HLL2360D \
      --prefix PATH ":" ${ stdenv.lib.makeBinPath [ ghostscript a2ps file gnused gnugrep coreutils which ] }
    '';

  meta = with stdenv.lib; {
    homepage = "https://www.brother.com/";
    description = "Brother HL-L2360DW combined print driver";
    license = licenses.unfree;
    platforms = [
      "x86_64-linux"
    ];
  };
}

I came across this issue with my brother L23500W(connected over WiF in a private network) and Dymo 450(connected via usb). The settings base is the CUPS daemon that needs to be running. This is how the user sends commands to the printer it has access to. But enabling CUPS is not enough, as it runs under its own user and for you to interact with it you will need to be part of the CUPS group. Without this setting I was unable to send any command to the printer(both of them). Also in case of a printer that does not use IPP you will probably need to add a driver for the printer as I did for the Dymo.

So my base settings were:

services.printing.enable = true;
services.printing.drivers = [ pkgs.cups-dymo ];
users.users.${config.userDefinedGlobalVariables.username} = {
extraGroups = [ “lp” ];
};

Now if you want to use a printer over the network without the need to install drivers you will need to add avahi to the mix. It is used(among other things) for CUPS to automatically discover IPP printers on the network.

services.avahi.enable = true;
services.avahi.nssmdns = true;
services.avahi.openFirewall = true;

Once I added the avahi settings my brother 23500 became available to me to print with.

for reference my source files

A handy command to debug the state of the printers is

lpstat -p

4 Likes

Thank you! The avchi lines worked for me on my Brother printer on wifi.