blob: c7f7a3164390e6c746b1e77e3fa27769a953d92a [file] [log] [blame]
Clemens Ladisch31ef9132011-03-15 07:53:21 +01001/*
2 * Connection Management Procedures (IEC 61883-1) helper functions
3 *
4 * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
5 * Licensed under the terms of the GNU General Public License, version 2.
6 */
7
8#include <linux/device.h>
9#include <linux/firewire.h>
10#include <linux/firewire-constants.h>
11#include <linux/module.h>
12#include <linux/sched.h>
13#include "lib.h"
14#include "iso-resources.h"
15#include "cmp.h"
16
Takashi Sakamotoa7fa0d02014-04-25 22:44:54 +090017/* MPR common fields */
18#define MPR_SPEED_MASK 0xc0000000
19#define MPR_SPEED_SHIFT 30
20#define MPR_XSPEED_MASK 0x00000060
21#define MPR_XSPEED_SHIFT 5
22#define MPR_PLUGS_MASK 0x0000001f
Clemens Ladisch31ef9132011-03-15 07:53:21 +010023
Takashi Sakamotoa7fa0d02014-04-25 22:44:54 +090024/* PCR common fields */
25#define PCR_ONLINE 0x80000000
26#define PCR_BCAST_CONN 0x40000000
27#define PCR_P2P_CONN_MASK 0x3f000000
28#define PCR_P2P_CONN_SHIFT 24
29#define PCR_CHANNEL_MASK 0x003f0000
30#define PCR_CHANNEL_SHIFT 16
Clemens Ladisch31ef9132011-03-15 07:53:21 +010031
32enum bus_reset_handling {
33 ABORT_ON_BUS_RESET,
34 SUCCEED_ON_BUS_RESET,
35};
36
Joe Perchesb9075fa2011-10-31 17:11:33 -070037static __printf(2, 3)
Clemens Ladisch31ef9132011-03-15 07:53:21 +010038void cmp_error(struct cmp_connection *c, const char *fmt, ...)
39{
40 va_list va;
41
42 va_start(va, fmt);
43 dev_err(&c->resources.unit->device, "%cPCR%u: %pV",
44 'i', c->pcr_index, &(struct va_format){ fmt, &va });
45 va_end(va);
46}
47
48static int pcr_modify(struct cmp_connection *c,
49 __be32 (*modify)(struct cmp_connection *c, __be32 old),
50 int (*check)(struct cmp_connection *c, __be32 pcr),
51 enum bus_reset_handling bus_reset_handling)
52{
Stefan Richterf30e6d32011-04-22 15:13:54 +020053 __be32 old_arg, buffer[2];
Clemens Ladisch31ef9132011-03-15 07:53:21 +010054 int err;
55
56 buffer[0] = c->last_pcr_value;
57 for (;;) {
58 old_arg = buffer[0];
59 buffer[1] = modify(c, buffer[0]);
60
Clemens Ladisch1b704852011-09-04 22:17:38 +020061 err = snd_fw_transaction(
62 c->resources.unit, TCODE_LOCK_COMPARE_SWAP,
Clemens Ladisch31ef9132011-03-15 07:53:21 +010063 CSR_REGISTER_BASE + CSR_IPCR(c->pcr_index),
Clemens Ladisch1b704852011-09-04 22:17:38 +020064 buffer, 8,
65 FW_FIXED_GENERATION | c->resources.generation);
Clemens Ladisch31ef9132011-03-15 07:53:21 +010066
Clemens Ladisch1b704852011-09-04 22:17:38 +020067 if (err < 0) {
68 if (err == -EAGAIN &&
69 bus_reset_handling == SUCCEED_ON_BUS_RESET)
70 err = 0;
71 return err;
72 }
Clemens Ladisch31ef9132011-03-15 07:53:21 +010073
Clemens Ladisch1b704852011-09-04 22:17:38 +020074 if (buffer[0] == old_arg) /* success? */
75 break;
76
77 if (check) {
78 err = check(c, buffer[0]);
79 if (err < 0)
80 return err;
81 }
Clemens Ladisch31ef9132011-03-15 07:53:21 +010082 }
83 c->last_pcr_value = buffer[1];
84
85 return 0;
Clemens Ladisch31ef9132011-03-15 07:53:21 +010086}
87
88
89/**
90 * cmp_connection_init - initializes a connection manager
91 * @c: the connection manager to initialize
92 * @unit: a unit of the target device
Takashi Sakamotoa7fa0d02014-04-25 22:44:54 +090093 * @pcr_index: the index of the iPCR/oPCR on the target device
Clemens Ladisch31ef9132011-03-15 07:53:21 +010094 */
95int cmp_connection_init(struct cmp_connection *c,
96 struct fw_unit *unit,
Takashi Sakamotoc68a1c62014-04-25 22:44:55 +090097 enum cmp_direction direction,
Takashi Sakamotoa7fa0d02014-04-25 22:44:54 +090098 unsigned int pcr_index)
Clemens Ladisch31ef9132011-03-15 07:53:21 +010099{
Takashi Sakamotoa7fa0d02014-04-25 22:44:54 +0900100 __be32 mpr_be;
101 u32 mpr;
Clemens Ladisch31ef9132011-03-15 07:53:21 +0100102 int err;
103
104 err = snd_fw_transaction(unit, TCODE_READ_QUADLET_REQUEST,
105 CSR_REGISTER_BASE + CSR_IMPR,
Takashi Sakamotoa7fa0d02014-04-25 22:44:54 +0900106 &mpr_be, 4, 0);
Clemens Ladisch31ef9132011-03-15 07:53:21 +0100107 if (err < 0)
108 return err;
Takashi Sakamotoa7fa0d02014-04-25 22:44:54 +0900109 mpr = be32_to_cpu(mpr_be);
Clemens Ladisch31ef9132011-03-15 07:53:21 +0100110
Takashi Sakamotoa7fa0d02014-04-25 22:44:54 +0900111 if (pcr_index >= (mpr & MPR_PLUGS_MASK))
Clemens Ladisch31ef9132011-03-15 07:53:21 +0100112 return -EINVAL;
113
Clemens Ladisch5b2599a2011-03-15 07:55:50 +0100114 err = fw_iso_resources_init(&c->resources, unit);
115 if (err < 0)
116 return err;
117
Clemens Ladisch31ef9132011-03-15 07:53:21 +0100118 c->connected = false;
119 mutex_init(&c->mutex);
Clemens Ladisch31ef9132011-03-15 07:53:21 +0100120 c->last_pcr_value = cpu_to_be32(0x80000000);
Takashi Sakamotoa7fa0d02014-04-25 22:44:54 +0900121 c->pcr_index = pcr_index;
122 c->max_speed = (mpr & MPR_SPEED_MASK) >> MPR_SPEED_SHIFT;
Clemens Ladisch31ef9132011-03-15 07:53:21 +0100123 if (c->max_speed == SCODE_BETA)
Takashi Sakamotoa7fa0d02014-04-25 22:44:54 +0900124 c->max_speed += (mpr & MPR_XSPEED_MASK) >> MPR_XSPEED_SHIFT;
Clemens Ladisch31ef9132011-03-15 07:53:21 +0100125
126 return 0;
127}
128EXPORT_SYMBOL(cmp_connection_init);
129
130/**
131 * cmp_connection_destroy - free connection manager resources
132 * @c: the connection manager
133 */
134void cmp_connection_destroy(struct cmp_connection *c)
135{
136 WARN_ON(c->connected);
137 mutex_destroy(&c->mutex);
138 fw_iso_resources_destroy(&c->resources);
139}
140EXPORT_SYMBOL(cmp_connection_destroy);
141
142
143static __be32 ipcr_set_modify(struct cmp_connection *c, __be32 ipcr)
144{
Takashi Sakamotoa7fa0d02014-04-25 22:44:54 +0900145 ipcr &= ~cpu_to_be32(PCR_BCAST_CONN |
146 PCR_P2P_CONN_MASK |
147 PCR_CHANNEL_MASK);
148 ipcr |= cpu_to_be32(1 << PCR_P2P_CONN_SHIFT);
149 ipcr |= cpu_to_be32(c->resources.channel << PCR_CHANNEL_SHIFT);
Clemens Ladisch31ef9132011-03-15 07:53:21 +0100150
151 return ipcr;
152}
153
Takashi Sakamotoa7fa0d02014-04-25 22:44:54 +0900154static int pcr_set_check(struct cmp_connection *c, __be32 pcr)
Clemens Ladisch31ef9132011-03-15 07:53:21 +0100155{
Takashi Sakamotoa7fa0d02014-04-25 22:44:54 +0900156 if (pcr & cpu_to_be32(PCR_BCAST_CONN |
157 PCR_P2P_CONN_MASK)) {
Clemens Ladisch31ef9132011-03-15 07:53:21 +0100158 cmp_error(c, "plug is already in use\n");
159 return -EBUSY;
160 }
Takashi Sakamotoa7fa0d02014-04-25 22:44:54 +0900161 if (!(pcr & cpu_to_be32(PCR_ONLINE))) {
Clemens Ladisch31ef9132011-03-15 07:53:21 +0100162 cmp_error(c, "plug is not on-line\n");
163 return -ECONNREFUSED;
164 }
165
166 return 0;
167}
168
169/**
170 * cmp_connection_establish - establish a connection to the target
171 * @c: the connection manager
172 * @max_payload_bytes: the amount of data (including CIP headers) per packet
173 *
174 * This function establishes a point-to-point connection from the local
175 * computer to the target by allocating isochronous resources (channel and
Takashi Sakamotoa7fa0d02014-04-25 22:44:54 +0900176 * bandwidth) and setting the target's input/output plug control register.
177 * When this function succeeds, the caller is responsible for starting
178 * transmitting packets.
Clemens Ladisch31ef9132011-03-15 07:53:21 +0100179 */
180int cmp_connection_establish(struct cmp_connection *c,
181 unsigned int max_payload_bytes)
182{
183 int err;
184
185 if (WARN_ON(c->connected))
186 return -EISCONN;
187
188 c->speed = min(c->max_speed,
189 fw_parent_device(c->resources.unit)->max_speed);
190
191 mutex_lock(&c->mutex);
192
193retry_after_bus_reset:
194 err = fw_iso_resources_allocate(&c->resources,
195 max_payload_bytes, c->speed);
196 if (err < 0)
197 goto err_mutex;
198
Takashi Sakamotoa7fa0d02014-04-25 22:44:54 +0900199 err = pcr_modify(c, ipcr_set_modify, pcr_set_check,
Clemens Ladisch31ef9132011-03-15 07:53:21 +0100200 ABORT_ON_BUS_RESET);
201 if (err == -EAGAIN) {
202 fw_iso_resources_free(&c->resources);
203 goto retry_after_bus_reset;
204 }
205 if (err < 0)
206 goto err_resources;
207
208 c->connected = true;
209
210 mutex_unlock(&c->mutex);
211
212 return 0;
213
214err_resources:
215 fw_iso_resources_free(&c->resources);
216err_mutex:
217 mutex_unlock(&c->mutex);
218
219 return err;
220}
221EXPORT_SYMBOL(cmp_connection_establish);
222
223/**
224 * cmp_connection_update - update the connection after a bus reset
225 * @c: the connection manager
226 *
Takashi Sakamotoa7fa0d02014-04-25 22:44:54 +0900227 * This function must be called from the driver's .update handler to
228 * reestablish any connection that might have been active.
Clemens Ladisch31ef9132011-03-15 07:53:21 +0100229 *
230 * Returns zero on success, or a negative error code. On an error, the
231 * connection is broken and the caller must stop transmitting iso packets.
232 */
233int cmp_connection_update(struct cmp_connection *c)
234{
235 int err;
236
237 mutex_lock(&c->mutex);
238
239 if (!c->connected) {
240 mutex_unlock(&c->mutex);
241 return 0;
242 }
243
244 err = fw_iso_resources_update(&c->resources);
245 if (err < 0)
246 goto err_unconnect;
247
Takashi Sakamotoa7fa0d02014-04-25 22:44:54 +0900248 err = pcr_modify(c, ipcr_set_modify, pcr_set_check,
Clemens Ladisch31ef9132011-03-15 07:53:21 +0100249 SUCCEED_ON_BUS_RESET);
250 if (err < 0)
251 goto err_resources;
252
253 mutex_unlock(&c->mutex);
254
255 return 0;
256
257err_resources:
258 fw_iso_resources_free(&c->resources);
259err_unconnect:
260 c->connected = false;
261 mutex_unlock(&c->mutex);
262
263 return err;
264}
265EXPORT_SYMBOL(cmp_connection_update);
266
Takashi Sakamotoa7fa0d02014-04-25 22:44:54 +0900267static __be32 pcr_break_modify(struct cmp_connection *c, __be32 pcr)
Clemens Ladisch31ef9132011-03-15 07:53:21 +0100268{
Takashi Sakamotoa7fa0d02014-04-25 22:44:54 +0900269 return pcr & ~cpu_to_be32(PCR_BCAST_CONN | PCR_P2P_CONN_MASK);
Clemens Ladisch31ef9132011-03-15 07:53:21 +0100270}
271
272/**
273 * cmp_connection_break - break the connection to the target
274 * @c: the connection manager
275 *
Takashi Sakamotoa7fa0d02014-04-25 22:44:54 +0900276 * This function deactives the connection in the target's input/output plug
277 * control register, and frees the isochronous resources of the connection.
278 * Before calling this function, the caller should cease transmitting packets.
Clemens Ladisch31ef9132011-03-15 07:53:21 +0100279 */
280void cmp_connection_break(struct cmp_connection *c)
281{
282 int err;
283
284 mutex_lock(&c->mutex);
285
286 if (!c->connected) {
287 mutex_unlock(&c->mutex);
288 return;
289 }
290
Takashi Sakamotoa7fa0d02014-04-25 22:44:54 +0900291 err = pcr_modify(c, pcr_break_modify, NULL, SUCCEED_ON_BUS_RESET);
Clemens Ladisch31ef9132011-03-15 07:53:21 +0100292 if (err < 0)
293 cmp_error(c, "plug is still connected\n");
294
295 fw_iso_resources_free(&c->resources);
296
297 c->connected = false;
298
299 mutex_unlock(&c->mutex);
300}
301EXPORT_SYMBOL(cmp_connection_break);