Commit bfb15a96 authored by Nícolas F. R. A. Prado's avatar Nícolas F. R. A. Prado
Browse files

Update literal markup in all posts to make use of the <code> tag

parent 5349ef68
Pipeline #234431306 passed with stages
in 35 seconds
......@@ -5,8 +5,6 @@ Configurando o mbsync para usar XOAUTH2
:date: 2020-07-30
:tags: xoauth2, mbsync, email
.. default-role:: code
Por um bom tempo eu usei o offlineimap_ para sincronizar meus emails entre os
provedores e o meu computador. Ter acesso a todos os meus emails sem precisar de
......@@ -41,24 +39,24 @@ Configuração
============
Para configurar o oauth2token eu segui as instruções no README_, que consistem
em basicamente criar dois arquivos json com as informações da conta (`client_id`
e `client_secret` devem ser configurados no provedor, no meu caso o Gmail) em um
diretório específico, executar `oauth2create <provedor> <conta>` e logar na
conta de email pelo navegador.
em basicamente criar dois arquivos json com as informações da conta
(``client_id`` e ``client_secret`` devem ser configurados no provedor, no meu
caso o Gmail) em um diretório específico, executar ``oauth2create <provedor>
<conta>`` e logar na conta de email pelo navegador.
Depois de ter configurado o oauth2token, configurar o mbsync pra usá-lo foi bem
simples. Eu apenas adicionei `PassCmd "oauth2get <provedor> <conta>"` na seção
`IMAPAccount` do meu `.mbsyncrc`, trocando `<provedor>` e `<conta>` pelos
valores que eu utilizei no `oauth2get`, claro. No meu caso não foi necessário
especificar o mecanismo de autenticação nessa mesma seção usando `AuthMechs
XOAUTH2`, já que XOAUTH2 é considerado o mecanismo mais seguro instalado, e
portanto é o padrão. O que eu precisei fazer foi adicionar `AuthMechs PLAIN` na
seção `IMAPAccount` das outras contas que eu não queria que utilizassem o
XOAUTH2.
simples. Eu apenas adicionei ``PassCmd "oauth2get <provedor> <conta>"`` na seção
``IMAPAccount`` do meu ``.mbsyncrc``, trocando ``<provedor>`` e ``<conta>``
pelos valores que eu utilizei no ``oauth2get``, claro. No meu caso não foi
necessário especificar o mecanismo de autenticação nessa mesma seção usando
``AuthMechs XOAUTH2``, já que XOAUTH2 é considerado o mecanismo mais seguro
instalado, e portanto é o padrão. O que eu precisei fazer foi adicionar
``AuthMechs PLAIN`` na seção ``IMAPAccount`` das outras contas que eu não queria
que utilizassem o XOAUTH2.
Por fim, executei o mbsync para a conta em que eu configurei o XOAUTH2 ("dac").
A saída verbosa (`-V`) mostra que ele autenticou usando XOAUTH2 e tudo funcionou
como esperado::
A saída verbosa (``-V``) mostra que ele autenticou usando XOAUTH2 e tudo
funcionou como esperado::
[nfraprado@ArchWay ~]$ mbsync -V dac
Reading configuration file /home/nfraprado/.mbsyncrc
......
......@@ -5,8 +5,6 @@ Setting up mbsync to work with XOAUTH2
:date: 2020-07-30
:tags: xoauth2, mbsync, email
.. default-role:: code
For a long time I used offlineimap_ to synchronize my emails between the email
providers and my computer. Having access to all my emails offline on my computer
......@@ -42,22 +40,22 @@ Configuration
To configure oauth2token I followed the instructions in the README_, which
consist of basically creating two json files with the account information
(`client_id` and `client_secret` should be setup with the provider, in my case
Gmail) in a specific directory, executing `oauth2create <provider> <account>`
and logging into the email account on the browser.
(``client_id`` and ``client_secret`` should be setup with the provider, in my
case Gmail) in a specific directory, executing ``oauth2create <provider>
<account>`` and logging into the email account on the browser.
Having set up oauth2token, configuring mbsync to use it was pretty simple. I
just added `PassCmd "oauth2get <provider> <account>"` in the `IMAPAccount`
section of my `.mbsyncrc`, obviously changing `<provider>` and `<account>` for
the values I configured in oauth2token. In my case it wasn't necessary to
specify the authentication mechanism in that same section with `AuthMechs
XOAUTH2`, since XOAUTH2 is considered the safest mechanism installed, so it is
the default. Rather I needed to use `AuthMechs PLAIN` in the `IMAPAccount`
just added ``PassCmd "oauth2get <provider> <account>"`` in the ``IMAPAccount``
section of my ``.mbsyncrc``, obviously changing ``<provider>`` and ``<account>``
for the values I configured in oauth2token. In my case it wasn't necessary to
specify the authentication mechanism in that same section with ``AuthMechs
XOAUTH2``, since XOAUTH2 is considered the safest mechanism installed, so it is
the default. Rather I needed to use ``AuthMechs PLAIN`` in the ``IMAPAccount``
section of the other accounts that I didn't want to use XOAUTH2 with.
Finally, I ran mbsync for the account that I configured XOAUTH2 with ("dac").
By making the output verbose (`-V`), it can be seen that it authenticated using
XOAUTH2 and everything worked as expected::
By making the output verbose (``-V``), it can be seen that it authenticated
using XOAUTH2 and everything worked as expected::
[nfraprado@ArchWay ~]$ mbsync -V dac
Reading configuration file /home/nfraprado/.mbsyncrc
......
......@@ -5,8 +5,6 @@ Geração de listas de reprodução de música com o MPD
:date: 2020-09-20
:tags: mpd, python
.. default-role:: code
Música é vida. Eu realmente amo ouvir música, apesar que não o mesmo tipo de
música o tempo todo. Mas na maioria das vezes, vai de tudo: Eu gosto de ouvir
......@@ -44,86 +42,86 @@ o que ela deve conter, e também o que ela não deve, e também poder combinar
critérios de outras listas usando operações de E e OU. Isso provavelmente
ficará mais claro mais à frente com os exemplos.
O código que implementa essa classe está em `mpd_playlist.py`, e contém o
O código que implementa essa classe está em ``mpd_playlist.py``, e contém o
seguinte:
.. include:: ../../code/mpd-playlists/mpd_playlist.py
:code: python
Aquele trecho com `MPDClient()` no começo é necessário para conectar ao banco de
dados do MPD para posteriormente obter todas as informações sobre as músicas.
Aquele trecho com ``MPDClient()`` no começo é necessário para conectar ao banco
de dados do MPD para posteriormente obter todas as informações sobre as músicas.
Ele é provido pelo pacote python-mpd2_.
O método `__init__()`, que é chamado quando um novo objeto é criado, pode
receber outra lista de reprodução como seu parâmetro `query` e nesse caso a nova
lista apenas as músicas da lista passada. Ele também pode receber um conjunto de
músicas (o que eu ainda nem cheguei a usar, mas fazia sentido suportar). E
finalmente, no caso mais comum, pode receber um dicionário contendo os
parâmetros de busca que vão ser usados para filtrar as músicas do MPD. Para
O método ``__init__()``, que é chamado quando um novo objeto é criado, pode
receber outra lista de reprodução como seu parâmetro ``query`` e nesse caso a
nova lista apenas as músicas da lista passada. Ele também pode receber um
conjunto de músicas (o que eu ainda nem cheguei a usar, mas fazia sentido
suportar). E finalmente, no caso mais comum, pode receber um dicionário contendo
os parâmetros de busca que vão ser usados para filtrar as músicas do MPD. Para
todos os casos, um nome pode opcionalmente ser passado (necessário para que a
lista possa ser salva).
Então, por exemplo, `músicas_paramore = MPDPlaylist({'artist': 'Paramore'})`
Então, por exemplo, ``músicas_paramore = MPDPlaylist({'artist': 'Paramore'})``
criaria uma lista de reprodução apenas com músicas do Paramore, e
`músicas_paramore2 = MPDPlaylist(músicas_paramore)` poderia ser usado para criar
uma cópia dessa lista. Esse segundo caso não parece tão útil mas é a base para
processar expressões, como veremos.
`__repr__()` só é usado para depurar. Ele especifica como o objeto é escrito na
tela, nesse caso, mostrando quais músicas a lista de reprodução contém.
`__or__()` e `__and__()` são chamadas quando duas listas de reprodução são
combinadas com OU (usando `|`) e com E (usando `&`), retornando uma lista que
contém a união (músicas das duas listas) e a intersecção (apenas músicas
presentes nas duas) respectivamente. `__neg__()` serve para negar a lista
(usando `-`), fazendo com que os parâmetros de busca especifiquem o que ela
``músicas_paramore2 = MPDPlaylist(músicas_paramore)`` poderia ser usado para
criar uma cópia dessa lista. Esse segundo caso não parece tão útil mas é a base
para processar expressões, como veremos.
``__repr__()`` só é usado para depurar. Ele especifica como o objeto é escrito
na tela, nesse caso, mostrando quais músicas a lista de reprodução contém.
``__or__()`` e ``__and__()`` são chamadas quando duas listas de reprodução são
combinadas com OU (usando ``|``) e com E (usando ``&``), retornando uma lista
que contém a união (músicas das duas listas) e a intersecção (apenas músicas
presentes nas duas) respectivamente. ``__neg__()`` serve para negar a lista
(usando ``-``), fazendo com que os parâmetros de busca especifiquem o que ela
**não** deve conter, e portanto que ela contenha tudo menos o que for indicado.
`query_songs()` obtém as músicas do MPD com base nos parâmetros de busca e as
``query_songs()`` obtém as músicas do MPD com base nos parâmetros de busca e as
salva dentro do objeto.
`write_to_file()` salva a lista de reprodução em um arquivo `.m3u` para que
``write_to_file()`` salva a lista de reprodução em um arquivo ``.m3u`` para que
possa posteriormente ser lido e as músicas reproduzidas pelo MPD. O nome do
arquivo é o que foi fornecido em `__init__`.
arquivo é o que foi fornecido em ``__init__``.
Agora, alguns exemplos.
Minhas listas de reprodução
===========================
Em outro arquivo, `playlists.py`, eu tenho a definição de todas as minhas listas
de reprodução usando a classe MPDPlaylist:
Em outro arquivo, ``playlists.py``, eu tenho a definição de todas as minhas
listas de reprodução usando a classe MPDPlaylist:
.. include:: ../../code/mpd-playlists/playlists.py
:code: python
As primeiras listas criadas são:
* `saved`, que contém todas as músicas da pasta `genres`, essencialmente minha
biblioteca de músicas;
* ``saved``, que contém todas as músicas da pasta ``genres``, essencialmente
minha biblioteca de músicas;
* `buffer`, com todas as músicas da pasta `buffer`, que são aquelas que ainda
estou ouvindo para decidir se gosto ou não;
* ``buffer``, com todas as músicas da pasta ``buffer``, que são aquelas que
ainda estou ouvindo para decidir se gosto ou não;
* `fvt` com todas as músicas de pastas começando com `%`, que são meus álbuns
favoritos
* ``fvt`` com todas as músicas de pastas começando com ``%``, que são meus
álbuns favoritos
Em seguida são definidas listas pra cada um dos gêneros. Algumas delas, `rock`,
`electronic`, `pop`, `post_rock` e `rap` são então combinadas usando o operador
OU (`|`) para criar a lista `common`. Isso signifca que essa lista de reprodução
contém as músicas de todas essas listas combinadas.
Em seguida são definidas listas pra cada um dos gêneros. Algumas delas,
``rock``, ``electronic``, ``pop``, ``post_rock`` e ``rap`` são então combinadas
usando o operador OU (``|``) para criar a lista ``common``. Isso signifca que
essa lista de reprodução contém as músicas de todas essas listas combinadas.
Aí a lista `not_soundtrack` é criada negando a lista `soundtrack`, então
Aí a lista ``not_soundtrack`` é criada negando a lista ``soundtrack``, então
aquela contém apenas as músicas não presentes nesta.
A lista `tdg` possui apenas músicas do artista `Three Days Grace`.
A lista ``tdg`` possui apenas músicas do artista ``Three Days Grace``.
A última lista, `background`, combina várias listas definidas anteriormente como
`classical`, e também listas anônimas como `PL({'artist': "Balmorhea"})` (que
são usadas apenas para criar essa lista, e não serão salvas como listas
independentes, portanto não são armazenadas em variáveis e também não precisam
de um nome como parâmetro), bem como remove músicas específicas como
`PL({'file': "genres/soundtrack/games/Portal - Still Alive.mp3"})`.
A última lista, ``background``, combina várias listas definidas anteriormente
como ``classical``, e também listas anônimas como ``PL({'artist':
"Balmorhea"})`` (que são usadas apenas para criar essa lista, e não serão salvas
como listas independentes, portanto não são armazenadas em variáveis e também
não precisam de um nome como parâmetro), bem como remove músicas específicas
como ``PL({'file': "genres/soundtrack/games/Portal - Still Alive.mp3"})``.
Não é a sintaxe mais sucinta de todas, mas também não chega a ser extensa e é
bastante flexível: serviu para tudo que eu precisava customizando as minhas
......@@ -134,19 +132,19 @@ Salvando as listas de reprodução
Talvez agora você esteja se perguntando como essas listas de reprodução são
escritas em arquivos se elas são apenas criadas e armazenadas em variáveis. A
resposta é: python é demais 😃. Esse é `gen_playlists.py`, o código que faz
resposta é: python é demais 😃. Esse é ``gen_playlists.py``, o código que faz
isso:
.. include:: ../../code/mpd-playlists/gen_playlists.py
:code: python
Como aquele comentário gentil em cima diz, `playlists = (pl for pl in
vars(playlists).values() if isinstance(pl, MPDPlaylist) and pl.name)` obtém
todas as variáveis do tipo `MPDPlaylist` de `playlists.py`, desde que tenham
nome. Aí laço `for` itera sobre elas e salva cada uma em um arquivo com seu
Como aquele comentário gentil em cima diz, ``playlists = (pl for pl in
vars(playlists).values() if isinstance(pl, MPDPlaylist) and pl.name)`` obtém
todas as variáveis do tipo ``MPDPlaylist`` de ``playlists.py``, desde que tenham
nome. Aí laço ``for`` itera sobre elas e salva cada uma em um arquivo com seu
nome.
Por fim, eu adicionei uma linha no `cron` para executar esse script todo
Por fim, eu adicionei uma linha no ``cron`` para executar esse script todo
domingo, atualizando minhas listas de reprodução.
A lista de reprodução 'newest'
......@@ -154,10 +152,10 @@ A lista de reprodução 'newest'
Como nada é perfeito, tem uma lista de reprodução que eu não consegui integrar
nessa infraestrutura e portanto ficou como um script em bash separado 🤢: a
lista `newest`. Ela contém as últimas 100 músicas adicionadas à minha
lista ``newest``. Ela contém as últimas 100 músicas adicionadas à minha
biblioteca.
Existem quatro tipos de datas em arquivos de acordo com o `stat`:
Existem quatro tipos de datas em arquivos de acordo com o ``stat``:
* data de criação do arquivo
* data do último acesso
......@@ -167,7 +165,8 @@ Existem quatro tipos de datas em arquivos de acordo com o `stat`:
Para obter as últimas músicas adicionadas, e não as últimas editadas (às vezes
eu edito os metadados de uma música, e não quero que isso interfira nessa
lista), eu precisava usar a data de criação, mas ela não é suportada pelo MPD,
então é por isso que estou fadado a usar esse script, `gen_playlist_newest.sh`:
então é por isso que estou fadado a usar esse script,
``gen_playlist_newest.sh``:
.. include:: ../../code/mpd-playlists/gen_playlist_newest.sh
:code: shell
......
......@@ -5,8 +5,6 @@ Playlist generation with MPD
:date: 2020-09-20
:tags: mpd, python
.. default-role:: code
Music is life. I really love listening to music, although not the same kind of
music all the time. Most of the time though, anything goes: I like to listen to
......@@ -40,17 +38,17 @@ should be in it, and also what shouldn't, and be able to combine criteria from
other playlists using AND and OR operations. This will probably become clearer
later with the examples.
The code implementing this class is in `mpd_playlist.py`, which is the
The code implementing this class is in ``mpd_playlist.py``, which is the
following:
.. include:: ../../code/mpd-playlists/mpd_playlist.py
:code: python
That `MPDClient()` thing in the beginning is needed to connect to the MPD
That ``MPDClient()`` thing in the beginning is needed to connect to the MPD
database to later retrieve all music information. It is provided by the
python-mpd2_ package.
The `__init__()` method, which is called when creating a new playlist object,
The ``__init__()`` method, which is called when creating a new playlist object,
can accept another playlist as its query parameter, in which case the new
playlist just copies the songs from the playlist passed. It can also receive a
set of songs (which I never actually used, but it made sense to support). And
......@@ -58,34 +56,35 @@ finally, for the most common use, it can receive a dictionary containing the
query that will be used to filter the music from MPD. For all cases, an optional
name can also be passed for the playlist (needed for the playlist to be saved).
So, for example, `paramore_playlist = MPDPlaylist({'artist': 'Paramore'})` would
create a playlist only with songs from Paramore, and then `paramore_playlist2 =
MPDPlaylist(paramore_playlist)` could be used to create a copy of that playlist.
This second case doesn't sound as useful but is the base of parsing expressions,
as we'll see.
So, for example, ``paramore_playlist = MPDPlaylist({'artist': 'Paramore'})``
would create a playlist only with songs from Paramore, and then
``paramore_playlist2 = MPDPlaylist(paramore_playlist)`` could be used to create
a copy of that playlist. This second case doesn't sound as useful but is the
base of parsing expressions, as we'll see.
`__repr__()` is just there for debugging. It specifies how the object is
``__repr__()`` is just there for debugging. It specifies how the object is
printed, showing the songs the playlist contains.
`__or__()` and `__and__()` are called when two playlists are OR'ed (using `|`)
and AND'ed (using `&`) together, yielding a playlist that contains the union
(songs from both playlists) and the intersection (only songs that were in both
playlists) respectively. `__neg__()` is for negating the playlist (using `-`),
making so that the query specifies what the playlist should **not** contain, so
it will contain everything but that.
``__or__()`` and ``__and__()`` are called when two playlists are OR'ed (using
``|``) and AND'ed (using ``&``) together, yielding a playlist that contains the
union (songs from both playlists) and the intersection (only songs that were in
both playlists) respectively. ``__neg__()`` is for negating the playlist (using
``-``), making so that the query specifies what the playlist should **not**
contain, so it will contain everything but that.
`query_songs()` uses the query to get all songs that match it from MPD and saves
them in the object.
``query_songs()`` uses the query to get all songs that match it from MPD and
saves them in the object.
`write_to_file()` saves the playlist in a `.m3u` file so that it can be later
read and played by MPD. The name of the file is the name given in `__init__`.
``write_to_file()`` saves the playlist in a ``.m3u`` file so that it can be
later read and played by MPD. The name of the file is the name given in
``__init__``.
Now, to some examples.
My playlists
============
In another file, `playlists.py`, I have all of my playlists defined using the
In another file, ``playlists.py``, I have all of my playlists defined using the
MPDPlaylist class:
.. include:: ../../code/mpd-playlists/playlists.py
......@@ -93,31 +92,31 @@ MPDPlaylist class:
The first playlists created are:
* `saved`, containing all songs that are in the `genres` folder, which is
* ``saved``, containing all songs that are in the ``genres`` folder, which is
essentially my music library;
* `buffer`, with all songs that are inside the `buffer` folder, which are the
ones I'm still listening to decide if I like or not;
* ``buffer``, with all songs that are inside the ``buffer`` folder, which are
the ones I'm still listening to decide if I like or not;
* `fvt` with all songs from folders beginning with a `%`, which are my favorite
albums.
* ``fvt`` with all songs from folders beginning with a ``%``, which are my
favorite albums.
Then there are playlists for each of the genres. Some of those, namely `rock`,
`electronic`, `pop`, `post_rock` and `rap` are then combined with the OR (`|`)
operator to create the `common` playlist. This means that this playlist contains
the music from all of those playlists combined.
Then there are playlists for each of the genres. Some of those, namely ``rock``,
``electronic``, ``pop``, ``post_rock`` and ``rap`` are then combined with the OR
(``|``) operator to create the ``common`` playlist. This means that this
playlist contains the music from all of those playlists combined.
Next, the `not_soundtrack` playlist is created by negating the `soundtrack`
Next, the ``not_soundtrack`` playlist is created by negating the ``soundtrack``
playlist, so the former only contains the music not present in the latter.
The `tdg` playlist only has music from the `Three Days Grace` artist.
The ``tdg`` playlist only has music from the ``Three Days Grace`` artist.
The last playlist, `background`, combines several previously defined playlists
like `classical`, and also anonymous playlists like `PL({'artist':
"Balmorhea"})` (which are just used to create this playlist, and won't be saved
The last playlist, ``background``, combines several previously defined playlists
like ``classical``, and also anonymous playlists like ``PL({'artist':
"Balmorhea"})`` (which are just used to create this playlist, and won't be saved
as playlists themselves, therefore they aren't saved in a variable and also
don't need a name parameter), while also removing specific songs like
`PL({'file': "genres/soundtrack/games/Portal - Still Alive.mp3"})`.
``PL({'file': "genres/soundtrack/games/Portal - Still Alive.mp3"})``.
This isn't the most succint syntax but it also isn't bad and is very flexible:
It served all my needs for customizing my playlists.
......@@ -127,28 +126,28 @@ Saving the playlists
Now, you might be wondering how those playlists are written to the files if they
are only created and saved in variables. The answer is: python is cool 😃. This
is `gen_playlists.py`, the cool script that does it:
is ``gen_playlists.py``, the cool script that does it:
.. include:: ../../code/mpd-playlists/gen_playlists.py
:code: python
As that nice comment above it says, `playlists = (pl for pl in
vars(playlists).values() if isinstance(pl, MPDPlaylist) and pl.name)` gets all
variables of type `MPDPlaylist` from `playlists.py`, as long as they have a
As that nice comment above it says, ``playlists = (pl for pl in
vars(playlists).values() if isinstance(pl, MPDPlaylist) and pl.name)`` gets all
variables of type ``MPDPlaylist`` from ``playlists.py``, as long as they have a
name. Then the for loop iterates over them and saves each playlist in a file
with its name.
Finally, I added a line in `cron` to run this script every sunday, updating my
Finally, I added a line in ``cron`` to run this script every sunday, updating my
playlists.
The 'newest' playlist
=====================
Since nothing is perfect, there's one playlist that I couldn't integrate in that
framework and was left as its own shell script 🤢: the `newest` playlist. It
framework and was left as its own shell script 🤢: the ``newest`` playlist. It
contains the 100 latest songs added to my music library.
There are four different timestamps for files according to `stat`:
There are four different timestamps for files according to ``stat``:
* time of file birth
* time of last access
......@@ -158,7 +157,7 @@ There are four different timestamps for files according to `stat`:
To get the latest songs added, and not the ones most recently edited (sometimes
I edit song metadata, and don't want it to interfere with this playlist), I
needed to use time of birth, but it isn't supported by MPD, so that's why I'm
stuck with this `gen_playlist_newest.sh` shell script:
stuck with this ``gen_playlist_newest.sh`` shell script:
.. include:: ../../code/mpd-playlists/gen_playlist_newest.sh
:code: shell
......
......@@ -5,7 +5,6 @@ Fazendo um cabo UART para o Nexus 5
:date: 2020-06-30
:tags: nexus5, eletrônica
.. default-role:: code
.. |---| replace:: —
......@@ -126,7 +125,7 @@ Testando
========
Depois de conectar o USB da placa no meu computador e o P2 no Nexus 5, eu abri o
console serial usando o `picocom` com uma taxa de transmissão de 115200:
console serial usando o ``picocom`` com uma taxa de transmissão de 115200:
.. code-block:: bash
......
......@@ -5,7 +5,6 @@ Making an UART cable for the Nexus 5
:date: 2020-06-30
:tags: nexus5, electronics
.. default-role:: code
.. |---| replace:: —
......@@ -123,7 +122,7 @@ Testing
=======
After connecting the board's USB to my computer and the audio jack to the Nexus
5, I opened a serial console using `picocom` with a 115200 baud rate:
5, I opened a serial console using ``picocom`` with a 115200 baud rate:
.. code-block:: bash
......
......@@ -5,8 +5,6 @@ Portando um driver de LED de flash para o kernel oficial
:date: 2020-11-20
:tags: nexus5, kernel
.. default-role:: code
Agora que eu já tinha `um cabo serial funcionando para o meu Nexus 5`_, eu
estava pronto para a ação em ajudar a trazer o Nexus 5 para o kernel oficial.
......@@ -35,7 +33,7 @@ Então eu fui passando por cada erro, usando as definições em ambos os kernels
como referência, e fazendo as mudanças necessárias.
Com o driver compilando com sucesso, eu adicionei o CONFIG para ele e o
habilitei como um módulo no *defconfig* usado pelo Nexus 5 (`qcom_defconfig`).
habilitei como um módulo no *defconfig* usado pelo Nexus 5 (``qcom_defconfig``).
Eu também vasculhei os arquivos de *devicetree* na árvore do kernel derivado
para descobrir de quais nós eu precisava para descrever o hardware do LED de
flash para o driver, e as propriedades necessárias neles, para que eu pudesse
......@@ -44,9 +42,9 @@ adicionar isso no kernel oficial.
**Obs**: Por conta da forma como *devicetrees* funcionam, pode ser que
propriedades de um mesmo nó estejam espalhadas por múltiplos arquivos. Só depois
eu descobri que eu poderia compilar o kernel derivado e gerar o fonte da
*devicetree* completa a partir do binário com `dtc -I dtb -O dts -o
*devicetree* completa a partir do binário com ``dtc -I dtb -O dts -o
downstream_dt.dts
kernel_lge_hammerhead/arch/arm/boot/msm8974-hammerhead-rev-11.dtb`.
kernel_lge_hammerhead/arch/arm/boot/msm8974-hammerhead-rev-11.dtb``.
Tendo um driver que compila, uma *devicetree* válida e as configurações
habilitando o driver, eu estava pronto para finalmente compilar meu kernel com o
......@@ -59,7 +57,7 @@ vinculou ao dispositivo do LED. Já que eu não tinha um bom entendimento de
comecei a pesquisar sobre.
Um ótimo material que eu encontrei foi `Solving Device Tree Issues`_ (`mais no
eLinux`_). Inclusive foi usando o script `dt_node_info` que essa apresentação
eLinux`_). Inclusive foi usando o script ``dt_node_info`` que essa apresentação
mostra que eu descobri que o dispositivo estava sendo carregado mas o driver
não. Além disso, as técnicas de depuração mostradas, como habilitar prints de
debug dinâmicos nas funções de *probe* me ajudaram a descobrir que a função
......@@ -74,11 +72,11 @@ nele, caso contrário eles nunca iriam se vincular. Então isso era uma das cois
que eu precisava mudar.
Mas fazer com que meu driver se registrasse no barramento *platform*, fazia com
que eu não tivesse mais o ponteiro de `spmi_device` que eu precisava para usar
que eu não tivesse mais o ponteiro de ``spmi_device`` que eu precisava para usar
as funções de SPMI para ler e escrever nos registradores. Mais uma vez, olhando
nos drivers em volta, como o `qcom-spmi-iadc`, eu percebi que tinha esse tal de
*regmap* que eu poderia usar para ler e escrever nos registradores por SPMI mas
sem ser específico ao SPMI. Eu fiz o sensato e decidi experimentar!
nos drivers em volta, como o ``qcom-spmi-iadc``, eu percebi que tinha esse tal
de *regmap* que eu poderia usar para ler e escrever nos registradores por SPMI
mas sem ser específico ao SPMI. Eu fiz o sensato e decidi experimentar!
Com `essas alterações feitas`_, o driver agora estava se vinculando ao
dispositivo, mas a função *probe* estava falhando com as seguintes mensagens:
......@@ -109,23 +107,23 @@ o próprio LED de flash estivesse com problema?
Então eu comecei a explorar o *sysfs* desse sistema para entender como ele
funcionava. Eu encontrei o regulador que estava sendo usado pelo LED, cujo
`status` ia para `enabled` sempre que eu ligava o LED usando `echo 1 >
/sys/class/leds/led\:flash_torch/brightness`.
``status`` ia para ``enabled`` sempre que eu ligava o LED usando ``echo 1 >
/sys/class/leds/led\:flash_torch/brightness``.
Tendo o nó do regulador e a *devicetree* no kernel derivado, e o driver do
regulador (`qcom_spmi-regulator`) e a *dtbinding* dele no kernel oficial, eu
regulador (``qcom_spmi-regulator``) e a *dtbinding* dele no kernel oficial, eu
comecei a fazer o trabalho de detetive.
Depois de um pouco de investigação eu finalmente descobri que, já que o endereço
do regulador era `0xa000`, o regulador chamado `8941_boost` na árvore derivada
na verdade `é conhecido como`_ `s4` na árvore oficial, ou também pelo apelido
`pm8941_5v`.
do regulador era ``0xa000``, o regulador chamado ``8941_boost`` na árvore
derivada na verdade `é conhecido como`_ ``s4`` na árvore oficial, ou também pelo
apelido ``pm8941_5v``.
Eu ainda tinha que descobrir a verdadeira identidade do outro regulador, mas
agora que eu já tinha ganhado um pouco de confiança de um trabalho de detetive
bem feito, e com a ajuda de algumas dicas da *devicetree*, eu apostei tudo que
que a verdadeira identidade do `pm8941_chg_boost` era `5vs1`, também chamado de
`pm8941_5vs1`.
que a verdadeira identidade do ``pm8941_chg_boost`` era ``5vs1``, também chamado
de ``pm8941_5vs1``.
`Salvar`_, gravar, testar, eeee... nada. Ainda sim não funcionou, mas eu
claramente tinha progredido. Agora a função *probe* estava sendo executada com
......@@ -153,10 +151,10 @@ estavam falhando:
[ 13.347622] leds_qpnp:qpnp_dump_regs: ===== led:flash_0 LED register dump end =====
Como eu estava confiante que a *devicetree* agora estava certa, eu voltei para o
código do driver. Eu espalhei alguns `pr_debug()` pela função de *probe* e
reparei que o valor do `reg`, que é lido da *devicetree* e usado como o endereço
de base para todas as operações de leitura e escrita, estava com `0`, sendo que
ele deveria estar com `0xd300`.
código do driver. Eu espalhei alguns ``pr_debug()`` pela função de *probe* e
reparei que o valor do ``reg``, que é lido da *devicetree* e usado como o
endereço de base para todas as operações de leitura e escrita, estava com ``0``,
sendo que ele deveria estar com ``0xd300``.
Ah. Sério?? Bom, não seria uma aventura completa sem eu adicionar meu próprio
bug, né? 😝
......
......@@ -5,8 +5,6 @@ Porting a flash LED driver upstream
:date: 2020-11-20
:tags: nexus5, kernel
.. default-role:: code
Now that I had `a working serial cable for my Nexus 5`_, I was ready to face
some action and help in upstreaming.
......@@ -34,7 +32,7 @@ each error, referencing the definitions on both the downstream and upstream
kernels, and made the necessary changes.
With the driver successfully compiling, I added a CONFIG for it and enabled it
as a module in the *defconfig* used by Nexus 5 (`qcom_defconfig`). I also went
as a module in the *defconfig* used by Nexus 5 (``qcom_defconfig``). I also went
through the *devicetree* files on the downstream tree to find which nodes I
needed to describe the flash LED hardware to the driver, and the properties
needed in them, so I could also add them upstream.
......@@ -42,8 +40,8 @@ needed in them, so I could also add them upstream.
**Note**: The design of *devicetrees* can make the properties of a node be
scattered across different files. Later I learned that I could compile the
downstream kernel and read the source for the complete tree from the blob with
`dtc -I dtb -O dts -o downstream_dt.dts
kernel_lge_hammerhead/arch/arm/boot/msm8974-hammerhead-rev-11.dtb`.
``dtc -I dtb -O dts -o downstream_dt.dts
kernel_lge_hammerhead/arch/arm/boot/msm8974-hammerhead-rev-11.dtb``.
With a driver that compiles, a valid *devicetree*, and configs enabling the
driver as a module, I was ready to finally compile my kernel with the driver and
......@@ -55,7 +53,7 @@ the binding between the devices and drivers worked, I started researching about
it.