Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * Connection Management Procedures (IEC 61883-1) helper functions
0004  *
0005  * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
0006  */
0007 
0008 #include <linux/device.h>
0009 #include <linux/firewire.h>
0010 #include <linux/firewire-constants.h>
0011 #include <linux/module.h>
0012 #include <linux/sched.h>
0013 #include "lib.h"
0014 #include "iso-resources.h"
0015 #include "cmp.h"
0016 
0017 /* MPR common fields */
0018 #define MPR_SPEED_MASK      0xc0000000
0019 #define MPR_SPEED_SHIFT     30
0020 #define MPR_XSPEED_MASK     0x00000060
0021 #define MPR_XSPEED_SHIFT    5
0022 #define MPR_PLUGS_MASK      0x0000001f
0023 
0024 /* PCR common fields */
0025 #define PCR_ONLINE      0x80000000
0026 #define PCR_BCAST_CONN      0x40000000
0027 #define PCR_P2P_CONN_MASK   0x3f000000
0028 #define PCR_P2P_CONN_SHIFT  24
0029 #define PCR_CHANNEL_MASK    0x003f0000
0030 #define PCR_CHANNEL_SHIFT   16
0031 
0032 /* oPCR specific fields */
0033 #define OPCR_XSPEED_MASK    0x00C00000
0034 #define OPCR_XSPEED_SHIFT   22
0035 #define OPCR_SPEED_MASK     0x0000C000
0036 #define OPCR_SPEED_SHIFT    14
0037 #define OPCR_OVERHEAD_ID_MASK   0x00003C00
0038 #define OPCR_OVERHEAD_ID_SHIFT  10
0039 
0040 enum bus_reset_handling {
0041     ABORT_ON_BUS_RESET,
0042     SUCCEED_ON_BUS_RESET,
0043 };
0044 
0045 static __printf(2, 3)
0046 void cmp_error(struct cmp_connection *c, const char *fmt, ...)
0047 {
0048     va_list va;
0049 
0050     va_start(va, fmt);
0051     dev_err(&c->resources.unit->device, "%cPCR%u: %pV",
0052         (c->direction == CMP_INPUT) ? 'i' : 'o',
0053         c->pcr_index, &(struct va_format){ fmt, &va });
0054     va_end(va);
0055 }
0056 
0057 static u64 mpr_address(struct cmp_connection *c)
0058 {
0059     if (c->direction == CMP_INPUT)
0060         return CSR_REGISTER_BASE + CSR_IMPR;
0061     else
0062         return CSR_REGISTER_BASE + CSR_OMPR;
0063 }
0064 
0065 static u64 pcr_address(struct cmp_connection *c)
0066 {
0067     if (c->direction == CMP_INPUT)
0068         return CSR_REGISTER_BASE + CSR_IPCR(c->pcr_index);
0069     else
0070         return CSR_REGISTER_BASE + CSR_OPCR(c->pcr_index);
0071 }
0072 
0073 static int pcr_modify(struct cmp_connection *c,
0074               __be32 (*modify)(struct cmp_connection *c, __be32 old),
0075               int (*check)(struct cmp_connection *c, __be32 pcr),
0076               enum bus_reset_handling bus_reset_handling)
0077 {
0078     __be32 old_arg, buffer[2];
0079     int err;
0080 
0081     buffer[0] = c->last_pcr_value;
0082     for (;;) {
0083         old_arg = buffer[0];
0084         buffer[1] = modify(c, buffer[0]);
0085 
0086         err = snd_fw_transaction(
0087                 c->resources.unit, TCODE_LOCK_COMPARE_SWAP,
0088                 pcr_address(c), buffer, 8,
0089                 FW_FIXED_GENERATION | c->resources.generation);
0090 
0091         if (err < 0) {
0092             if (err == -EAGAIN &&
0093                 bus_reset_handling == SUCCEED_ON_BUS_RESET)
0094                 err = 0;
0095             return err;
0096         }
0097 
0098         if (buffer[0] == old_arg) /* success? */
0099             break;
0100 
0101         if (check) {
0102             err = check(c, buffer[0]);
0103             if (err < 0)
0104                 return err;
0105         }
0106     }
0107     c->last_pcr_value = buffer[1];
0108 
0109     return 0;
0110 }
0111 
0112 
0113 /**
0114  * cmp_connection_init - initializes a connection manager
0115  * @c: the connection manager to initialize
0116  * @unit: a unit of the target device
0117  * @direction: input or output
0118  * @pcr_index: the index of the iPCR/oPCR on the target device
0119  */
0120 int cmp_connection_init(struct cmp_connection *c,
0121             struct fw_unit *unit,
0122             enum cmp_direction direction,
0123             unsigned int pcr_index)
0124 {
0125     __be32 mpr_be;
0126     u32 mpr;
0127     int err;
0128 
0129     c->direction = direction;
0130     err = snd_fw_transaction(unit, TCODE_READ_QUADLET_REQUEST,
0131                  mpr_address(c), &mpr_be, 4, 0);
0132     if (err < 0)
0133         return err;
0134     mpr = be32_to_cpu(mpr_be);
0135 
0136     if (pcr_index >= (mpr & MPR_PLUGS_MASK))
0137         return -EINVAL;
0138 
0139     err = fw_iso_resources_init(&c->resources, unit);
0140     if (err < 0)
0141         return err;
0142 
0143     c->connected = false;
0144     mutex_init(&c->mutex);
0145     c->last_pcr_value = cpu_to_be32(0x80000000);
0146     c->pcr_index = pcr_index;
0147     c->max_speed = (mpr & MPR_SPEED_MASK) >> MPR_SPEED_SHIFT;
0148     if (c->max_speed == SCODE_BETA)
0149         c->max_speed += (mpr & MPR_XSPEED_MASK) >> MPR_XSPEED_SHIFT;
0150 
0151     return 0;
0152 }
0153 EXPORT_SYMBOL(cmp_connection_init);
0154 
0155 /**
0156  * cmp_connection_check_used - check connection is already esablished or not
0157  * @c: the connection manager to be checked
0158  * @used: the pointer to store the result of checking the connection
0159  */
0160 int cmp_connection_check_used(struct cmp_connection *c, bool *used)
0161 {
0162     __be32 pcr;
0163     int err;
0164 
0165     err = snd_fw_transaction(
0166             c->resources.unit, TCODE_READ_QUADLET_REQUEST,
0167             pcr_address(c), &pcr, 4, 0);
0168     if (err >= 0)
0169         *used = !!(pcr & cpu_to_be32(PCR_BCAST_CONN |
0170                          PCR_P2P_CONN_MASK));
0171 
0172     return err;
0173 }
0174 EXPORT_SYMBOL(cmp_connection_check_used);
0175 
0176 /**
0177  * cmp_connection_destroy - free connection manager resources
0178  * @c: the connection manager
0179  */
0180 void cmp_connection_destroy(struct cmp_connection *c)
0181 {
0182     WARN_ON(c->connected);
0183     mutex_destroy(&c->mutex);
0184     fw_iso_resources_destroy(&c->resources);
0185 }
0186 EXPORT_SYMBOL(cmp_connection_destroy);
0187 
0188 int cmp_connection_reserve(struct cmp_connection *c,
0189                unsigned int max_payload_bytes)
0190 {
0191     int err;
0192 
0193     mutex_lock(&c->mutex);
0194 
0195     if (WARN_ON(c->resources.allocated)) {
0196         err = -EBUSY;
0197         goto end;
0198     }
0199 
0200     c->speed = min(c->max_speed,
0201                fw_parent_device(c->resources.unit)->max_speed);
0202 
0203     err = fw_iso_resources_allocate(&c->resources, max_payload_bytes,
0204                     c->speed);
0205 end:
0206     mutex_unlock(&c->mutex);
0207 
0208     return err;
0209 }
0210 EXPORT_SYMBOL(cmp_connection_reserve);
0211 
0212 void cmp_connection_release(struct cmp_connection *c)
0213 {
0214     mutex_lock(&c->mutex);
0215     fw_iso_resources_free(&c->resources);
0216     mutex_unlock(&c->mutex);
0217 }
0218 EXPORT_SYMBOL(cmp_connection_release);
0219 
0220 static __be32 ipcr_set_modify(struct cmp_connection *c, __be32 ipcr)
0221 {
0222     ipcr &= ~cpu_to_be32(PCR_BCAST_CONN |
0223                  PCR_P2P_CONN_MASK |
0224                  PCR_CHANNEL_MASK);
0225     ipcr |= cpu_to_be32(1 << PCR_P2P_CONN_SHIFT);
0226     ipcr |= cpu_to_be32(c->resources.channel << PCR_CHANNEL_SHIFT);
0227 
0228     return ipcr;
0229 }
0230 
0231 static int get_overhead_id(struct cmp_connection *c)
0232 {
0233     int id;
0234 
0235     /*
0236      * apply "oPCR overhead ID encoding"
0237      * the encoding table can convert up to 512.
0238      * here the value over 512 is converted as the same way as 512.
0239      */
0240     for (id = 1; id < 16; id++) {
0241         if (c->resources.bandwidth_overhead < (id << 5))
0242             break;
0243     }
0244     if (id == 16)
0245         id = 0;
0246 
0247     return id;
0248 }
0249 
0250 static __be32 opcr_set_modify(struct cmp_connection *c, __be32 opcr)
0251 {
0252     unsigned int spd, xspd;
0253 
0254     /* generate speed and extended speed field value */
0255     if (c->speed > SCODE_400) {
0256         spd  = SCODE_800;
0257         xspd = c->speed - SCODE_800;
0258     } else {
0259         spd = c->speed;
0260         xspd = 0;
0261     }
0262 
0263     opcr &= ~cpu_to_be32(PCR_BCAST_CONN |
0264                  PCR_P2P_CONN_MASK |
0265                  OPCR_XSPEED_MASK |
0266                  PCR_CHANNEL_MASK |
0267                  OPCR_SPEED_MASK |
0268                  OPCR_OVERHEAD_ID_MASK);
0269     opcr |= cpu_to_be32(1 << PCR_P2P_CONN_SHIFT);
0270     opcr |= cpu_to_be32(xspd << OPCR_XSPEED_SHIFT);
0271     opcr |= cpu_to_be32(c->resources.channel << PCR_CHANNEL_SHIFT);
0272     opcr |= cpu_to_be32(spd << OPCR_SPEED_SHIFT);
0273     opcr |= cpu_to_be32(get_overhead_id(c) << OPCR_OVERHEAD_ID_SHIFT);
0274 
0275     return opcr;
0276 }
0277 
0278 static int pcr_set_check(struct cmp_connection *c, __be32 pcr)
0279 {
0280     if (pcr & cpu_to_be32(PCR_BCAST_CONN |
0281                   PCR_P2P_CONN_MASK)) {
0282         cmp_error(c, "plug is already in use\n");
0283         return -EBUSY;
0284     }
0285     if (!(pcr & cpu_to_be32(PCR_ONLINE))) {
0286         cmp_error(c, "plug is not on-line\n");
0287         return -ECONNREFUSED;
0288     }
0289 
0290     return 0;
0291 }
0292 
0293 /**
0294  * cmp_connection_establish - establish a connection to the target
0295  * @c: the connection manager
0296  *
0297  * This function establishes a point-to-point connection from the local
0298  * computer to the target by allocating isochronous resources (channel and
0299  * bandwidth) and setting the target's input/output plug control register.
0300  * When this function succeeds, the caller is responsible for starting
0301  * transmitting packets.
0302  */
0303 int cmp_connection_establish(struct cmp_connection *c)
0304 {
0305     int err;
0306 
0307     mutex_lock(&c->mutex);
0308 
0309     if (WARN_ON(c->connected)) {
0310         mutex_unlock(&c->mutex);
0311         return -EISCONN;
0312     }
0313 
0314 retry_after_bus_reset:
0315     if (c->direction == CMP_OUTPUT)
0316         err = pcr_modify(c, opcr_set_modify, pcr_set_check,
0317                  ABORT_ON_BUS_RESET);
0318     else
0319         err = pcr_modify(c, ipcr_set_modify, pcr_set_check,
0320                  ABORT_ON_BUS_RESET);
0321 
0322     if (err == -EAGAIN) {
0323         err = fw_iso_resources_update(&c->resources);
0324         if (err >= 0)
0325             goto retry_after_bus_reset;
0326     }
0327     if (err >= 0)
0328         c->connected = true;
0329 
0330     mutex_unlock(&c->mutex);
0331 
0332     return err;
0333 }
0334 EXPORT_SYMBOL(cmp_connection_establish);
0335 
0336 /**
0337  * cmp_connection_update - update the connection after a bus reset
0338  * @c: the connection manager
0339  *
0340  * This function must be called from the driver's .update handler to
0341  * reestablish any connection that might have been active.
0342  *
0343  * Returns zero on success, or a negative error code.  On an error, the
0344  * connection is broken and the caller must stop transmitting iso packets.
0345  */
0346 int cmp_connection_update(struct cmp_connection *c)
0347 {
0348     int err;
0349 
0350     mutex_lock(&c->mutex);
0351 
0352     if (!c->connected) {
0353         mutex_unlock(&c->mutex);
0354         return 0;
0355     }
0356 
0357     err = fw_iso_resources_update(&c->resources);
0358     if (err < 0)
0359         goto err_unconnect;
0360 
0361     if (c->direction == CMP_OUTPUT)
0362         err = pcr_modify(c, opcr_set_modify, pcr_set_check,
0363                  SUCCEED_ON_BUS_RESET);
0364     else
0365         err = pcr_modify(c, ipcr_set_modify, pcr_set_check,
0366                  SUCCEED_ON_BUS_RESET);
0367 
0368     if (err < 0)
0369         goto err_unconnect;
0370 
0371     mutex_unlock(&c->mutex);
0372 
0373     return 0;
0374 
0375 err_unconnect:
0376     c->connected = false;
0377     mutex_unlock(&c->mutex);
0378 
0379     return err;
0380 }
0381 EXPORT_SYMBOL(cmp_connection_update);
0382 
0383 static __be32 pcr_break_modify(struct cmp_connection *c, __be32 pcr)
0384 {
0385     return pcr & ~cpu_to_be32(PCR_BCAST_CONN | PCR_P2P_CONN_MASK);
0386 }
0387 
0388 /**
0389  * cmp_connection_break - break the connection to the target
0390  * @c: the connection manager
0391  *
0392  * This function deactives the connection in the target's input/output plug
0393  * control register, and frees the isochronous resources of the connection.
0394  * Before calling this function, the caller should cease transmitting packets.
0395  */
0396 void cmp_connection_break(struct cmp_connection *c)
0397 {
0398     int err;
0399 
0400     mutex_lock(&c->mutex);
0401 
0402     if (!c->connected) {
0403         mutex_unlock(&c->mutex);
0404         return;
0405     }
0406 
0407     err = pcr_modify(c, pcr_break_modify, NULL, SUCCEED_ON_BUS_RESET);
0408     if (err < 0)
0409         cmp_error(c, "plug is still connected\n");
0410 
0411     c->connected = false;
0412 
0413     mutex_unlock(&c->mutex);
0414 }
0415 EXPORT_SYMBOL(cmp_connection_break);