blob: deb494d6e78553d625c1772f5e79a9704e5ae3ed [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 Sakamotoa7fa0d02014-04-25 22:44:54 +090097 unsigned int pcr_index)
Clemens Ladisch31ef9132011-03-15 07:53:21 +010098{
Takashi Sakamotoa7fa0d02014-04-25 22:44:54 +090099 __be32 mpr_be;
100 u32 mpr;
Clemens Ladisch31ef9132011-03-15 07:53:21 +0100101 int err;
102
103 err = snd_fw_transaction(unit, TCODE_READ_QUADLET_REQUEST,
104 CSR_REGISTER_BASE + CSR_IMPR,
Takashi Sakamotoa7fa0d02014-04-25 22:44:54 +0900105 &mpr_be, 4, 0);
Clemens Ladisch31ef9132011-03-15 07:53:21 +0100106 if (err < 0)
107 return err;
Takashi Sakamotoa7fa0d02014-04-25 22:44:54 +0900108 mpr = be32_to_cpu(mpr_be);
Clemens Ladisch31ef9132011-03-15 07:53:21 +0100109
Takashi Sakamotoa7fa0d02014-04-25 22:44:54 +0900110 if (pcr_index >= (mpr & MPR_PLUGS_MASK))
Clemens Ladisch31ef9132011-03-15 07:53:21 +0100111 return -EINVAL;
112
Clemens Ladisch5b2599a2011-03-15 07:55:50 +0100113 err = fw_iso_resources_init(&c->resources, unit);
114 if (err < 0)
115 return err;
116
Clemens Ladisch31ef9132011-03-15 07:53:21 +0100117 c->connected = false;
118 mutex_init(&c->mutex);
Clemens Ladisch31ef9132011-03-15 07:53:21 +0100119 c->last_pcr_value = cpu_to_be32(0x80000000);
Takashi Sakamotoa7fa0d02014-04-25 22:44:54 +0900120 c->pcr_index = pcr_index;
121 c->max_speed = (mpr & MPR_SPEED_MASK) >> MPR_SPEED_SHIFT;
Clemens Ladisch31ef9132011-03-15 07:53:21 +0100122 if (c->max_speed == SCODE_BETA)
Takashi Sakamotoa7fa0d02014-04-25 22:44:54 +0900123 c->max_speed += (mpr & MPR_XSPEED_MASK) >> MPR_XSPEED_SHIFT;
Clemens Ladisch31ef9132011-03-15 07:53:21 +0100124
125 return 0;
126}
127EXPORT_SYMBOL(cmp_connection_init);
128
129/**
130 * cmp_connection_destroy - free connection manager resources
131 * @c: the connection manager
132 */
133void cmp_connection_destroy(struct cmp_connection *c)
134{
135 WARN_ON(c->connected);
136 mutex_destroy(&c->mutex);
137 fw_iso_resources_destroy(&c->resources);
138}
139EXPORT_SYMBOL(cmp_connection_destroy);
140
141
142static __be32 ipcr_set_modify(struct cmp_connection *c, __be32 ipcr)
143{
Takashi Sakamotoa7fa0d02014-04-25 22:44:54 +0900144 ipcr &= ~cpu_to_be32(PCR_BCAST_CONN |
145 PCR_P2P_CONN_MASK |
146 PCR_CHANNEL_MASK);
147 ipcr |= cpu_to_be32(1 << PCR_P2P_CONN_SHIFT);
148 ipcr |= cpu_to_be32(c->resources.channel << PCR_CHANNEL_SHIFT);
Clemens Ladisch31ef9132011-03-15 07:53:21 +0100149
150 return ipcr;
151}
152
Takashi Sakamotoa7fa0d02014-04-25 22:44:54 +0900153static int pcr_set_check(struct cmp_connection *c, __be32 pcr)
Clemens Ladisch31ef9132011-03-15 07:53:21 +0100154{
Takashi Sakamotoa7fa0d02014-04-25 22:44:54 +0900155 if (pcr & cpu_to_be32(PCR_BCAST_CONN |
156 PCR_P2P_CONN_MASK)) {
Clemens Ladisch31ef9132011-03-15 07:53:21 +0100157 cmp_error(c, "plug is already in use\n");
158 return -EBUSY;
159 }
Takashi Sakamotoa7fa0d02014-04-25 22:44:54 +0900160 if (!(pcr & cpu_to_be32(PCR_ONLINE))) {
Clemens Ladisch31ef9132011-03-15 07:53:21 +0100161 cmp_error(c, "plug is not on-line\n");
162 return -ECONNREFUSED;
163 }
164
165 return 0;
166}
167
168/**
169 * cmp_connection_establish - establish a connection to the target
170 * @c: the connection manager
171 * @max_payload_bytes: the amount of data (including CIP headers) per packet
172 *
173 * This function establishes a point-to-point connection from the local
174 * computer to the target by allocating isochronous resources (channel and
Takashi Sakamotoa7fa0d02014-04-25 22:44:54 +0900175 * bandwidth) and setting the target's input/output plug control register.
176 * When this function succeeds, the caller is responsible for starting
177 * transmitting packets.
Clemens Ladisch31ef9132011-03-15 07:53:21 +0100178 */
179int cmp_connection_establish(struct cmp_connection *c,
180 unsigned int max_payload_bytes)
181{
182 int err;
183
184 if (WARN_ON(c->connected))
185 return -EISCONN;
186
187 c->speed = min(c->max_speed,
188 fw_parent_device(c->resources.unit)->max_speed);
189
190 mutex_lock(&c->mutex);
191
192retry_after_bus_reset:
193 err = fw_iso_resources_allocate(&c->resources,
194 max_payload_bytes, c->speed);
195 if (err < 0)
196 goto err_mutex;
197
Takashi Sakamotoa7fa0d02014-04-25 22:44:54 +0900198 err = pcr_modify(c, ipcr_set_modify, pcr_set_check,
Clemens Ladisch31ef9132011-03-15 07:53:21 +0100199 ABORT_ON_BUS_RESET);
200 if (err == -EAGAIN) {
201 fw_iso_resources_free(&c->resources);
202 goto retry_after_bus_reset;
203 }
204 if (err < 0)
205 goto err_resources;
206
207 c->connected = true;
208
209 mutex_unlock(&c->mutex);
210
211 return 0;
212
213err_resources:
214 fw_iso_resources_free(&c->resources);
215err_mutex:
216 mutex_unlock(&c->mutex);
217
218 return err;
219}
220EXPORT_SYMBOL(cmp_connection_establish);
221
222/**
223 * cmp_connection_update - update the connection after a bus reset
224 * @c: the connection manager
225 *
Takashi Sakamotoa7fa0d02014-04-25 22:44:54 +0900226 * This function must be called from the driver's .update handler to
227 * reestablish any connection that might have been active.
Clemens Ladisch31ef9132011-03-15 07:53:21 +0100228 *
229 * Returns zero on success, or a negative error code. On an error, the
230 * connection is broken and the caller must stop transmitting iso packets.
231 */
232int cmp_connection_update(struct cmp_connection *c)
233{
234 int err;
235
236 mutex_lock(&c->mutex);
237
238 if (!c->connected) {
239 mutex_unlock(&c->mutex);
240 return 0;
241 }
242
243 err = fw_iso_resources_update(&c->resources);
244 if (err < 0)
245 goto err_unconnect;
246
Takashi Sakamotoa7fa0d02014-04-25 22:44:54 +0900247 err = pcr_modify(c, ipcr_set_modify, pcr_set_check,
Clemens Ladisch31ef9132011-03-15 07:53:21 +0100248 SUCCEED_ON_BUS_RESET);
249 if (err < 0)
250 goto err_resources;
251
252 mutex_unlock(&c->mutex);
253
254 return 0;
255
256err_resources:
257 fw_iso_resources_free(&c->resources);
258err_unconnect:
259 c->connected = false;
260 mutex_unlock(&c->mutex);
261
262 return err;
263}
264EXPORT_SYMBOL(cmp_connection_update);
265
Takashi Sakamotoa7fa0d02014-04-25 22:44:54 +0900266static __be32 pcr_break_modify(struct cmp_connection *c, __be32 pcr)
Clemens Ladisch31ef9132011-03-15 07:53:21 +0100267{
Takashi Sakamotoa7fa0d02014-04-25 22:44:54 +0900268 return pcr & ~cpu_to_be32(PCR_BCAST_CONN | PCR_P2P_CONN_MASK);
Clemens Ladisch31ef9132011-03-15 07:53:21 +0100269}
270
271/**
272 * cmp_connection_break - break the connection to the target
273 * @c: the connection manager
274 *
Takashi Sakamotoa7fa0d02014-04-25 22:44:54 +0900275 * This function deactives the connection in the target's input/output plug
276 * control register, and frees the isochronous resources of the connection.
277 * Before calling this function, the caller should cease transmitting packets.
Clemens Ladisch31ef9132011-03-15 07:53:21 +0100278 */
279void cmp_connection_break(struct cmp_connection *c)
280{
281 int err;
282
283 mutex_lock(&c->mutex);
284
285 if (!c->connected) {
286 mutex_unlock(&c->mutex);
287 return;
288 }
289
Takashi Sakamotoa7fa0d02014-04-25 22:44:54 +0900290 err = pcr_modify(c, pcr_break_modify, NULL, SUCCEED_ON_BUS_RESET);
Clemens Ladisch31ef9132011-03-15 07:53:21 +0100291 if (err < 0)
292 cmp_error(c, "plug is still connected\n");
293
294 fw_iso_resources_free(&c->resources);
295
296 c->connected = false;
297
298 mutex_unlock(&c->mutex);
299}
300EXPORT_SYMBOL(cmp_connection_break);