1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * digi00x-transaction.c - a part of driver for Digidesign Digi 002/003 family |
4 | * |
5 | * Copyright (c) 2014-2015 Takashi Sakamoto |
6 | */ |
7 | |
8 | #include <sound/asound.h> |
9 | #include "digi00x.h" |
10 | |
11 | static void handle_unknown_message(struct snd_dg00x *dg00x, |
12 | unsigned long long offset, __be32 *buf) |
13 | { |
14 | unsigned long flags; |
15 | |
16 | spin_lock_irqsave(&dg00x->lock, flags); |
17 | dg00x->msg = be32_to_cpu(*buf); |
18 | spin_unlock_irqrestore(lock: &dg00x->lock, flags); |
19 | |
20 | wake_up(&dg00x->hwdep_wait); |
21 | } |
22 | |
23 | static void handle_message(struct fw_card *card, struct fw_request *request, |
24 | int tcode, int destination, int source, |
25 | int generation, unsigned long long offset, |
26 | void *data, size_t length, void *callback_data) |
27 | { |
28 | struct snd_dg00x *dg00x = callback_data; |
29 | __be32 *buf = (__be32 *)data; |
30 | |
31 | fw_send_response(card, request, RCODE_COMPLETE); |
32 | |
33 | if (offset == dg00x->async_handler.offset) |
34 | handle_unknown_message(dg00x, offset, buf); |
35 | } |
36 | |
37 | int snd_dg00x_transaction_reregister(struct snd_dg00x *dg00x) |
38 | { |
39 | struct fw_device *device = fw_parent_device(dg00x->unit); |
40 | __be32 data[2]; |
41 | |
42 | /* Unknown. 4bytes. */ |
43 | data[0] = cpu_to_be32((device->card->node_id << 16) | |
44 | (dg00x->async_handler.offset >> 32)); |
45 | data[1] = cpu_to_be32(dg00x->async_handler.offset); |
46 | return snd_fw_transaction(unit: dg00x->unit, TCODE_WRITE_BLOCK_REQUEST, |
47 | DG00X_ADDR_BASE + DG00X_OFFSET_MESSAGE_ADDR, |
48 | buffer: &data, length: sizeof(data), flags: 0); |
49 | } |
50 | |
51 | void snd_dg00x_transaction_unregister(struct snd_dg00x *dg00x) |
52 | { |
53 | if (dg00x->async_handler.callback_data == NULL) |
54 | return; |
55 | |
56 | fw_core_remove_address_handler(handler: &dg00x->async_handler); |
57 | |
58 | dg00x->async_handler.callback_data = NULL; |
59 | } |
60 | |
61 | int snd_dg00x_transaction_register(struct snd_dg00x *dg00x) |
62 | { |
63 | static const struct fw_address_region resp_register_region = { |
64 | .start = 0xffffe0000000ull, |
65 | .end = 0xffffe000ffffull, |
66 | }; |
67 | int err; |
68 | |
69 | dg00x->async_handler.length = 4; |
70 | dg00x->async_handler.address_callback = handle_message; |
71 | dg00x->async_handler.callback_data = dg00x; |
72 | |
73 | err = fw_core_add_address_handler(handler: &dg00x->async_handler, |
74 | region: &resp_register_region); |
75 | if (err < 0) |
76 | return err; |
77 | |
78 | err = snd_dg00x_transaction_reregister(dg00x); |
79 | if (err < 0) |
80 | snd_dg00x_transaction_unregister(dg00x); |
81 | |
82 | return err; |
83 | } |
84 | |