162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * helpers for managing a buffer for many packets 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (c) Clemens Ladisch <clemens@ladisch.de> 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/firewire.h> 962306a36Sopenharmony_ci#include <linux/export.h> 1062306a36Sopenharmony_ci#include <linux/slab.h> 1162306a36Sopenharmony_ci#include "packets-buffer.h" 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci/** 1462306a36Sopenharmony_ci * iso_packets_buffer_init - allocates the memory for packets 1562306a36Sopenharmony_ci * @b: the buffer structure to initialize 1662306a36Sopenharmony_ci * @unit: the device at the other end of the stream 1762306a36Sopenharmony_ci * @count: the number of packets 1862306a36Sopenharmony_ci * @packet_size: the (maximum) size of a packet, in bytes 1962306a36Sopenharmony_ci * @direction: %DMA_TO_DEVICE or %DMA_FROM_DEVICE 2062306a36Sopenharmony_ci */ 2162306a36Sopenharmony_ciint iso_packets_buffer_init(struct iso_packets_buffer *b, struct fw_unit *unit, 2262306a36Sopenharmony_ci unsigned int count, unsigned int packet_size, 2362306a36Sopenharmony_ci enum dma_data_direction direction) 2462306a36Sopenharmony_ci{ 2562306a36Sopenharmony_ci unsigned int packets_per_page, pages; 2662306a36Sopenharmony_ci unsigned int i, page_index, offset_in_page; 2762306a36Sopenharmony_ci void *p; 2862306a36Sopenharmony_ci int err; 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci b->packets = kmalloc_array(count, sizeof(*b->packets), GFP_KERNEL); 3162306a36Sopenharmony_ci if (!b->packets) { 3262306a36Sopenharmony_ci err = -ENOMEM; 3362306a36Sopenharmony_ci goto error; 3462306a36Sopenharmony_ci } 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci packet_size = L1_CACHE_ALIGN(packet_size); 3762306a36Sopenharmony_ci packets_per_page = PAGE_SIZE / packet_size; 3862306a36Sopenharmony_ci if (WARN_ON(!packets_per_page)) { 3962306a36Sopenharmony_ci err = -EINVAL; 4062306a36Sopenharmony_ci goto err_packets; 4162306a36Sopenharmony_ci } 4262306a36Sopenharmony_ci pages = DIV_ROUND_UP(count, packets_per_page); 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci err = fw_iso_buffer_init(&b->iso_buffer, fw_parent_device(unit)->card, 4562306a36Sopenharmony_ci pages, direction); 4662306a36Sopenharmony_ci if (err < 0) 4762306a36Sopenharmony_ci goto err_packets; 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci for (i = 0; i < count; ++i) { 5062306a36Sopenharmony_ci page_index = i / packets_per_page; 5162306a36Sopenharmony_ci p = page_address(b->iso_buffer.pages[page_index]); 5262306a36Sopenharmony_ci offset_in_page = (i % packets_per_page) * packet_size; 5362306a36Sopenharmony_ci b->packets[i].buffer = p + offset_in_page; 5462306a36Sopenharmony_ci b->packets[i].offset = page_index * PAGE_SIZE + offset_in_page; 5562306a36Sopenharmony_ci } 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci return 0; 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_cierr_packets: 6062306a36Sopenharmony_ci kfree(b->packets); 6162306a36Sopenharmony_cierror: 6262306a36Sopenharmony_ci return err; 6362306a36Sopenharmony_ci} 6462306a36Sopenharmony_ciEXPORT_SYMBOL(iso_packets_buffer_init); 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci/** 6762306a36Sopenharmony_ci * iso_packets_buffer_destroy - frees packet buffer resources 6862306a36Sopenharmony_ci * @b: the buffer structure to free 6962306a36Sopenharmony_ci * @unit: the device at the other end of the stream 7062306a36Sopenharmony_ci */ 7162306a36Sopenharmony_civoid iso_packets_buffer_destroy(struct iso_packets_buffer *b, 7262306a36Sopenharmony_ci struct fw_unit *unit) 7362306a36Sopenharmony_ci{ 7462306a36Sopenharmony_ci fw_iso_buffer_destroy(&b->iso_buffer, fw_parent_device(unit)->card); 7562306a36Sopenharmony_ci kfree(b->packets); 7662306a36Sopenharmony_ci} 7762306a36Sopenharmony_ciEXPORT_SYMBOL(iso_packets_buffer_destroy); 78