linux/sound/firewire/motu/motu-transaction.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * motu-transaction.c - a part of driver for MOTU FireWire series
   4 *
   5 * Copyright (c) 2015-2017 Takashi Sakamoto <o-takashi@sakamocchi.jp>
   6 */
   7
   8
   9#include "motu.h"
  10
  11#define SND_MOTU_ADDR_BASE      0xfffff0000000ULL
  12#define ASYNC_ADDR_HI  0x0b04
  13#define ASYNC_ADDR_LO  0x0b08
  14
  15int snd_motu_transaction_read(struct snd_motu *motu, u32 offset, __be32 *reg,
  16                              size_t size)
  17{
  18        int tcode;
  19
  20        if (size % sizeof(__be32) > 0 || size <= 0)
  21                return -EINVAL;
  22        if (size == sizeof(__be32))
  23                tcode = TCODE_READ_QUADLET_REQUEST;
  24        else
  25                tcode = TCODE_READ_BLOCK_REQUEST;
  26
  27        return snd_fw_transaction(motu->unit, tcode,
  28                                  SND_MOTU_ADDR_BASE + offset, reg, size, 0);
  29}
  30
  31int snd_motu_transaction_write(struct snd_motu *motu, u32 offset, __be32 *reg,
  32                               size_t size)
  33{
  34        int tcode;
  35
  36        if (size % sizeof(__be32) > 0 || size <= 0)
  37                return -EINVAL;
  38        if (size == sizeof(__be32))
  39                tcode = TCODE_WRITE_QUADLET_REQUEST;
  40        else
  41                tcode = TCODE_WRITE_BLOCK_REQUEST;
  42
  43        return snd_fw_transaction(motu->unit, tcode,
  44                                  SND_MOTU_ADDR_BASE + offset, reg, size, 0);
  45}
  46
  47static void handle_message(struct fw_card *card, struct fw_request *request,
  48                           int tcode, int destination, int source,
  49                           int generation, unsigned long long offset,
  50                           void *data, size_t length, void *callback_data)
  51{
  52        struct snd_motu *motu = callback_data;
  53        __be32 *buf = (__be32 *)data;
  54        unsigned long flags;
  55
  56        if (tcode != TCODE_WRITE_QUADLET_REQUEST) {
  57                fw_send_response(card, request, RCODE_COMPLETE);
  58                return;
  59        }
  60
  61        if (offset != motu->async_handler.offset || length != 4) {
  62                fw_send_response(card, request, RCODE_ADDRESS_ERROR);
  63                return;
  64        }
  65
  66        spin_lock_irqsave(&motu->lock, flags);
  67        motu->msg = be32_to_cpu(*buf);
  68        spin_unlock_irqrestore(&motu->lock, flags);
  69
  70        fw_send_response(card, request, RCODE_COMPLETE);
  71
  72        wake_up(&motu->hwdep_wait);
  73}
  74
  75int snd_motu_transaction_reregister(struct snd_motu *motu)
  76{
  77        struct fw_device *device = fw_parent_device(motu->unit);
  78        __be32 data;
  79        int err;
  80
  81        if (motu->async_handler.callback_data == NULL)
  82                return -EINVAL;
  83
  84        /* Register messaging address. Block transaction is not allowed. */
  85        data = cpu_to_be32((device->card->node_id << 16) |
  86                           (motu->async_handler.offset >> 32));
  87        err = snd_motu_transaction_write(motu, ASYNC_ADDR_HI, &data,
  88                                         sizeof(data));
  89        if (err < 0)
  90                return err;
  91
  92        data = cpu_to_be32(motu->async_handler.offset);
  93        return snd_motu_transaction_write(motu, ASYNC_ADDR_LO, &data,
  94                                          sizeof(data));
  95}
  96
  97int snd_motu_transaction_register(struct snd_motu *motu)
  98{
  99        static const struct fw_address_region resp_register_region = {
 100                .start  = 0xffffe0000000ull,
 101                .end    = 0xffffe000ffffull,
 102        };
 103        int err;
 104
 105        /* Perhaps, 4 byte messages are transferred. */
 106        motu->async_handler.length = 4;
 107        motu->async_handler.address_callback = handle_message;
 108        motu->async_handler.callback_data = motu;
 109
 110        err = fw_core_add_address_handler(&motu->async_handler,
 111                                          &resp_register_region);
 112        if (err < 0)
 113                return err;
 114
 115        err = snd_motu_transaction_reregister(motu);
 116        if (err < 0) {
 117                fw_core_remove_address_handler(&motu->async_handler);
 118                motu->async_handler.address_callback = NULL;
 119        }
 120
 121        return err;
 122}
 123
 124void snd_motu_transaction_unregister(struct snd_motu *motu)
 125{
 126        __be32 data;
 127
 128        if (motu->async_handler.address_callback != NULL)
 129                fw_core_remove_address_handler(&motu->async_handler);
 130        motu->async_handler.address_callback = NULL;
 131
 132        /* Unregister the address. */
 133        data = cpu_to_be32(0x00000000);
 134        snd_motu_transaction_write(motu, ASYNC_ADDR_HI, &data, sizeof(data));
 135        snd_motu_transaction_write(motu, ASYNC_ADDR_LO, &data, sizeof(data));
 136}
 137