Второе дыхание для Samsung ML-1615: AirPrint через LXC на Proxmox

Есть у меня дома лазерник Samsung ML-1615 — древний, 2005 года, но печатает исправно и тонер ещё не кончился, выкидывать жалко. Проблема одна: подключается только по USB, и который год он был воткнут в комп на столе — печать «с телефона» означала «переслать себе файл и подойти к компу». Решил это исправить: AirPrint через маленький LXC-контейнер на Proxmox. Оказалось не так просто, как «apt install cups» — принтер сначала соврал iPhone, что умеет двустороннюю печать, потом стал мигать красным и есть страницы. Расскажу по порядку.

Схема

Принтер воткнут по USB прямо в хост Proxmox. Отдельный privileged LXC-контейнер (512 МБ RAM хватает за глаза) получает USB по пробросу, крутит CUPS и объявляет очередь по mDNS/DNS-SD через Avahi — iPhone и Mac видят его в сети как обычный AirPrint-принтер.

Схема пути задания печати: iPhone и Mac по mDNS и IPP на порт 631, далее CUPS и splix внутри LXC-контейнера на хосте Proxmox, проброшенный /dev/bus/usb и принтер Samsung ML-1615
Путь задания печати: от кнопки на телефоне до бумаги

Первая мелкая засада: ML-1615 внутри опознаётся как «ML-1610 Mono Laser Printer» — тот же движок, та же прошивка, просто перелейбленный корпус. И говорит он не PCL и не PostScript, а SPL2/QPDL — фирменным языком Samsung, для которого в Linux есть только один драйвер: printer-driver-splix.

USB-проброс в LXC

Контейнер сделан privileged, чтобы не возиться с udev-правилами под непривилегированным UID-сдвигом. В /etc/pve/lxc/<id>.conf:

lxc.cgroup2.devices.allow: c 189:* rwm
lxc.cgroup2.devices.allow: c 180:* rwm
lxc.mount.entry: /dev/bus/usb dev/bus/usb none bind,optional,create=dir
lxc.mount.entry: /dev/usb dev/usb none bind,optional,create=dir

Суть в том, чтобы забиндить весь /dev/bus/usb, а не конкретное устройство — тогда переподключение принтера или смена номера USB-порта не требует лезть в конфиг. И важно проверить, что на самом хосте Proxmox не висит своего CUPS/cupsd — иначе он first-come-first-served захватит принтер, а контейнер получит унылое «device busy».

Пакеты внутри контейнера — стандартный набор:

apt install cups cups-filters cups-filters-core-drivers \
    printer-driver-splix avahi-daemon avahi-utils dbus

cups-filters-core-drivers — отдельно подчеркну, обязателен. Без него не объявляется MIME-тип image/urf, и печать с iPhone молча не работает, даже если очередь прекрасно видна в сети.

В cupsd.conf — слушать не только localhost, разрешить локалку и включить публикацию через Avahi:

Listen 0.0.0.0:631
BrowseLocalProtocols dnssd
# + Allow @LOCAL в <Location /> и <Location /admin>

cupsctl --remote-admin --share-printers

И сама очередь:

lpadmin -p ML1615 -E \
  -v "usb://Samsung/ML-1610?serial=<серийник>" \
  -m "drv:///splix-samsung.drv/ml1610.ppd" \
  -D "Samsung ML-1615 (AirPrint)" \
  -o printer-is-shared=true -o media=a4

media=a4 выставил сразу — иначе очередь по умолчанию встаёт на американский Letter, и потом удивляешься, почему поля съехали.

На этом моменте всё выглядело идеально: тестовая страница ушла, avahi-browse показывает очередь, iPhone принтер видит. Победа? Как бы не так — она началась именно тут.

Задание «completed», а бумага не выходит

Первый настоящий документ с iPhone (три страницы, PDF) CUPS честно отчитал как успешный — а принтер ничего не напечатал. Включил LogLevel debug и пошёл разбирать по слоям.

1. Задержка в 25-30 секунд на каждое задание

Растровый фильтр gstoraster для каждого задания стучится в DBus, к org.freedesktop.ColorManager, за цветовым профилем. А служба colord в LXC падает с 226/NAMESPACE — классическое ограничение контейнера на mount-namespacing systemd-юнитов с харденингом. DBus честно ждёт активации сервиса до таймаута (25 секунд), потом фильтр продолжает без профиля — не страшно для чёрно-белого принтера, но каждое задание тормозит почти на полминуты.

Лечится одной командой — сервис для монохромного принтера попросту не нужен:

systemctl mask colord

Маскирование даёт DBus мгновенный отказ вместо ожидания таймаута несуществующего сервиса — время created→completed упало с 60-90 секунд до 37.

2. Ядро тоже хочет дружить с принтером

На хосте Proxmox родной драйвер ядра usblp сам подхватывает USB-интерфейс принтера, конкурируя с прямым libusb-доступом backend’а CUPS. В логе это выглядело так: данные полностью ушли (Sent N bytes), а потом — Got USB transaction timeout during read и сброс принтера.

Отвязал руками и закрепил udev-правилом, чтобы не повторялось при переподключении:

# руками, сразу
echo -n '1-1:1.0' > /sys/bus/usb/drivers/usblp/unbind

# /etc/udev/rules.d/99-no-usblp-ml1610.rules
ACTION=="bind", SUBSYSTEM=="usb", DRIVER=="usblp", \
  ATTRS{idVendor}=="04e8", ATTRS{idProduct}=="3268", \
  RUN+="/bin/sh -c 'echo -n $kernel > /sys/bus/usb/drivers/usblp/unbind'"

Забавный нюанс: после отвязки usblp точно тот же паттерн read-timeout/reset иногда всё равно проскакивал в логах на следующих тестовых заданиях. То есть конфликт с драйвером ядра был реальным и его стоило устранить, но первопричиной пропавшей печати он не был — это, похоже, штатное поведение backend’а CUPS именно для этой модели. Настоящая причина крылась глубже.

3. Мигающий красный светодиод = принтеру не хватило памяти

У ML-1610/1615 по документации Samsung мигающий (не сплошной!) красный означает переполнение буфера: «If the printer does not have enough memory… simplify the page layout». А памяти там — смешные 2 МБ. Документ, отрендеренный HeadlessChrome в PDF с тяжёлой графикой, на дефолтных 600 dpi — это уже растр порядка 4300×6800 пикселей на A4, то есть мегабайты на страницу. Первая страница напечаталась, дальше принтер захлебнулся.

Временное лечение нашлось сразу — понизить разрешение:

lpadmin -p ML1615 -o Resolution=300dpi

На 300 dpi тот же файл пропечатался целиком без единого сбоя.

4. Почему с айфона нельзя просто выбрать пониже разрешение

Логичный вопрос: раз проблема в разрешении, пусть пользователь просто ставит 300 dpi в диалоге печати, когда видит, что документ сложный. Не вышло — и тут открылась забавная асимметрия. Компьютер под Windows, печатающий на ту же очередь через ipps://, реально передаёт в IPP-запросе атрибут printer-resolution, и CUPS честно матчит его на PPD-опцию. А AirPrint с iOS этот атрибут вообще не шлёт — только грубый print-quality (draft/normal/best), который наш PPD никак не связывает с DPI. Сколько ни листай варианты в диалоге печати на телефоне — там просто нет контрола, который реально долетит до Resolution. С сервера явный lp -o Resolution=600dpi работает штатно — дело не в CUPS, а в том, что iOS для этой очереди в принципе не умеет попросить нужное DPI.

Автоматику «сам определи, влезет или нет» тоже сделать не вышло: логи заведомо успешного (300 dpi) и заведомо провального (600 dpi, тот же файл) задания выглядят абсолютно одинаково — Job completed, backend отчитал «отправлено N байт» без единой ошибки в обоих случаях. Переполнение случается уже внутри самого принтера, после того как USB-передача целиком завершилась — программная сторона физически не получает об этом никакого сигнала обратно.

В итоге решил осознанно: дефолт очереди — 600 dpi (качество важнее), а если конкретный документ не допечатался и принтер мигает красным — вручную даю команду на 300 dpi, перепечатываю, потом при желании верну обратно.

5. Принтер, который врал про дуплекс

Отдельная находка: у ML-1610 физически нет автоматической двусторонней печати — бюджетная модель 2005 года, в механике попросту нет узла переворота листа. Но универсальный PPD драйвера splix всё равно объявлял опцию Duplex: None/DuplexNoTumble/DuplexTumble — и это транслировалось прямо в AirPrint-объявление по mDNS (TXT-запись показывала Duplex=T). То есть принтер буквально врал айфону, что умеет печатать с двух сторон.

Сопоставление логов показало, что запрос двусторонней печати определённо усугублял и без того шаткое положение принтера на грани его 2 МБ памяти. Полечил хирургически — вычистил лишние опции прямо из установленного PPD:

# в /etc/cups/ppd/ML1615.ppd убрать:
# *Duplex DuplexNoTumble...
# *Duplex DuplexTumble...
# оставить только:
# *Duplex None/Off (1-Sided)

rm /var/cache/cups/ML1615.data /var/cache/cups/ML1615.strings
systemctl restart cups avahi-daemon

После этого lpoptions -p ML1615 -l показывает только *None, а TXT-запись AirPrint больше не врёт про дуплекс. Единственный нюанс — любая будущая переустановка драйвера через lpadmin -m перезатрёт PPD системным шаблоном, и правку придётся повторять.

Итог

Принтер 2005 года печатает по AirPrint с айфона и мака через крошечный LXC на Proxmox, вообще без компа-посредника. Ушло на это куда больше времени, чем «apt install cups» — но каждая находка по пути была по-своему занятной: DBus, ждущий несуществующий сервис; ядро, дерущееся с CUPS за один и тот же USB-интерфейс; принтер, безбожно завышающий свои возможности перед телефоном; и айфон, который в принципе не умеет попросить нужное разрешение печати. Если у вас пылится такой же ветеран под столом — восстанавливать его в бытовом железе вокруг Proxmox определённо стоит.

Шпаргалка на будущее, если печать вдруг встанет колом:

  • «device busy» → проверить, что CUPS не поднялся на хосте Proxmox или в другом контейнере с тем же пробросом;
  • принтер виден в avahi внутри контейнера, но не с телефона → устройства не в одном L2-сегменте, mDNS не маршрутизируется;
  • мигает красный во время/после печати → переполнение памяти, штатно для этой серии, лечится понижением DPI;
  • сплошной красный (не мигающий) → это уже механика: бумага/замятие/тонер, тут логи не помогут;
  • каждое задание тормозит на ~25 секунд → проверить, что colord замаскирован.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *