1c781c06d119d04601727f2fbc30151e6760d536dKristian Høgsberg/* 2c781c06d119d04601727f2fbc30151e6760d536dKristian Høgsberg * Char device for device raw access 319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg * 4c781c06d119d04601727f2fbc30151e6760d536dKristian Høgsberg * Copyright (C) 2005-2007 Kristian Hoegsberg <krh@bitplanet.net> 519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg * 619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg * This program is free software; you can redistribute it and/or modify 719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg * it under the terms of the GNU General Public License as published by 819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg * the Free Software Foundation; either version 2 of the License, or 919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg * (at your option) any later version. 1019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg * 1119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg * This program is distributed in the hope that it will be useful, 1219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg * but WITHOUT ANY WARRANTY; without even the implied warranty of 1319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 1419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg * GNU General Public License for more details. 1519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg * 1619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg * You should have received a copy of the GNU General Public License 1719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg * along with this program; if not, write to the Free Software Foundation, 1819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 1919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg */ 2019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 21eb5b35a560510efc6bb62f05c3c82e9596cdfafeStefan Richter#include <linux/bug.h> 22be5bbd6756b44602a3f281af05c2f416fa9bd1c6Stefan Richter#include <linux/compat.h> 23be5bbd6756b44602a3f281af05c2f416fa9bd1c6Stefan Richter#include <linux/delay.h> 24be5bbd6756b44602a3f281af05c2f416fa9bd1c6Stefan Richter#include <linux/device.h> 250b6c4857f7684f6d3f59e0506f62953575346978Stefan Richter#include <linux/dma-mapping.h> 26be5bbd6756b44602a3f281af05c2f416fa9bd1c6Stefan Richter#include <linux/errno.h> 2777c9a5daa9c4d9b37812c9c69c7bcbb3f9399c3cStefan Richter#include <linux/firewire.h> 28be5bbd6756b44602a3f281af05c2f416fa9bd1c6Stefan Richter#include <linux/firewire-cdev.h> 29be5bbd6756b44602a3f281af05c2f416fa9bd1c6Stefan Richter#include <linux/idr.h> 304a9bde9b8ab55a2bb51b57cad215a97bcf80bae2Stefan Richter#include <linux/irqflags.h> 31b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter#include <linux/jiffies.h> 3219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg#include <linux/kernel.h> 33fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter#include <linux/kref.h> 34be5bbd6756b44602a3f281af05c2f416fa9bd1c6Stefan Richter#include <linux/mm.h> 35be5bbd6756b44602a3f281af05c2f416fa9bd1c6Stefan Richter#include <linux/module.h> 36d67cfb9613f373d76daa2c8d209629601424ca12Stefan Richter#include <linux/mutex.h> 3719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg#include <linux/poll.h> 38ae2a97661482c1d0f1aa41b837da95054d0e9a1bStefan Richter#include <linux/sched.h> /* required for linux/wait.h */ 395a0e3ad6af8660be21ca98a971cd00f331318c05Tejun Heo#include <linux/slab.h> 40cf417e5494582453c033d8cac9e1352e74215435Jay Fenlason#include <linux/spinlock.h> 41281e20323ab72180137824a298ee9e21e6f9acf6Stefan Richter#include <linux/string.h> 42be5bbd6756b44602a3f281af05c2f416fa9bd1c6Stefan Richter#include <linux/time.h> 43e034d242593f12533c11742ce38c245a33e57dc7Stefan Richter#include <linux/uaccess.h> 44be5bbd6756b44602a3f281af05c2f416fa9bd1c6Stefan Richter#include <linux/vmalloc.h> 45be5bbd6756b44602a3f281af05c2f416fa9bd1c6Stefan Richter#include <linux/wait.h> 46b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter#include <linux/workqueue.h> 47be5bbd6756b44602a3f281af05c2f416fa9bd1c6Stefan Richter 48be5bbd6756b44602a3f281af05c2f416fa9bd1c6Stefan Richter 4977c9a5daa9c4d9b37812c9c69c7bcbb3f9399c3cStefan Richter#include "core.h" 5019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 51604f45167824e18ad5766e51ecf1d4d65f15118dStefan Richter/* 52604f45167824e18ad5766e51ecf1d4d65f15118dStefan Richter * ABI version history is documented in linux/firewire-cdev.h. 53604f45167824e18ad5766e51ecf1d4d65f15118dStefan Richter */ 5418d627113b830cda80792e96b28341bcd41cf40cClemens Ladisch#define FW_CDEV_KERNEL_VERSION 5 558e2b2b46ea4ca5ef790dddf78b360ed736a62d7cStefan Richter#define FW_CDEV_VERSION_EVENT_REQUEST2 4 568e2b2b46ea4ca5ef790dddf78b360ed736a62d7cStefan Richter#define FW_CDEV_VERSION_ALLOCATE_REGION_END 4 570699a73af3811b66b1ab5650575acee5eea841abClemens Ladisch#define FW_CDEV_VERSION_AUTO_FLUSH_ISO_OVERFLOW 5 58604f45167824e18ad5766e51ecf1d4d65f15118dStefan Richter 5919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsbergstruct client { 60344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg u32 version; 6119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg struct fw_device *device; 6245ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason 6319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg spinlock_t lock; 6445ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason bool in_shutdown; 6545ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason struct idr resource_idr; 6619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg struct list_head event_list; 6719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg wait_queue_head_t wait; 685a5e62da9be255439e8ce59f96828775b7b33374Clemens Ladisch wait_queue_head_t tx_flush_wait; 69da8ecffaed434a12930f652898f9e86d1c2abc3eKristian Høgsberg u64 bus_reset_closure; 709aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg 7119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg struct fw_iso_context *iso_context; 72abaa5743e340c23922d92c9a5a6753ea3ae71e58Kristian Høgsberg u64 iso_closure; 739aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg struct fw_iso_buffer buffer; 749aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg unsigned long vm_start; 750b6c4857f7684f6d3f59e0506f62953575346978Stefan Richter bool buffer_is_mapped; 7697bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg 77bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter struct list_head phy_receiver_link; 78bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter u64 phy_receiver_closure; 79bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter 8097bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg struct list_head link; 81fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter struct kref kref; 8219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg}; 8319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 84fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richterstatic inline void client_get(struct client *client) 85fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter{ 86fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter kref_get(&client->kref); 87fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter} 88fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter 89fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richterstatic void client_release(struct kref *kref) 90fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter{ 91fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter struct client *client = container_of(kref, struct client, kref); 92fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter 93fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter fw_device_put(client->device); 94fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter kfree(client); 95fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter} 96fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter 97fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richterstatic void client_put(struct client *client) 98fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter{ 99fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter kref_put(&client->kref, client_release); 100fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter} 101fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter 10297c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richterstruct client_resource; 10397c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richtertypedef void (*client_resource_release_fn_t)(struct client *, 10497c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter struct client_resource *); 10597c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richterstruct client_resource { 10697c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter client_resource_release_fn_t release; 10797c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter int handle; 10897c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter}; 10997c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter 11097c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richterstruct address_handler_resource { 11197c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter struct client_resource resource; 11297c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter struct fw_address_handler handler; 11397c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter __u64 closure; 11497c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter struct client *client; 11597c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter}; 11697c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter 11797c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richterstruct outbound_transaction_resource { 11897c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter struct client_resource resource; 11997c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter struct fw_transaction transaction; 12097c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter}; 12197c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter 12297c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richterstruct inbound_transaction_resource { 12397c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter struct client_resource resource; 12408bd34c98d631fe85744d4c920c80f48a1d95f54Jay Fenlason struct fw_card *card; 12597c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter struct fw_request *request; 12697c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter void *data; 12797c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter size_t length; 12897c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter}; 12997c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter 13097c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richterstruct descriptor_resource { 13197c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter struct client_resource resource; 13297c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter struct fw_descriptor descriptor; 13397c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter u32 data[0]; 13497c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter}; 13597c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter 136b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richterstruct iso_resource { 137b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter struct client_resource resource; 138b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter struct client *client; 139b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter /* Schedule work and access todo only with client->lock held. */ 140b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter struct delayed_work work; 1411ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter enum {ISO_RES_ALLOC, ISO_RES_REALLOC, ISO_RES_DEALLOC, 1421ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter ISO_RES_ALLOC_ONCE, ISO_RES_DEALLOC_ONCE,} todo; 143b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter int generation; 144b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter u64 channels; 145b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter s32 bandwidth; 146b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter struct iso_resource_event *e_alloc, *e_dealloc; 147b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter}; 148b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter 149b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richterstatic void release_iso_resource(struct client *, struct client_resource *); 150b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter 1519fb551bf72929b316abb6d96cfb2ec05e896042aStefan Richterstatic void schedule_iso_resource(struct iso_resource *r, unsigned long delay) 1529fb551bf72929b316abb6d96cfb2ec05e896042aStefan Richter{ 1539fb551bf72929b316abb6d96cfb2ec05e896042aStefan Richter client_get(r->client); 154105e53f863c04e1d9e5bb34bf753c9fdbce6a60cStefan Richter if (!queue_delayed_work(fw_workqueue, &r->work, delay)) 1559fb551bf72929b316abb6d96cfb2ec05e896042aStefan Richter client_put(r->client); 1569fb551bf72929b316abb6d96cfb2ec05e896042aStefan Richter} 1579fb551bf72929b316abb6d96cfb2ec05e896042aStefan Richter 1589fb551bf72929b316abb6d96cfb2ec05e896042aStefan Richterstatic void schedule_if_iso_resource(struct client_resource *resource) 1599fb551bf72929b316abb6d96cfb2ec05e896042aStefan Richter{ 1609fb551bf72929b316abb6d96cfb2ec05e896042aStefan Richter if (resource->release == release_iso_resource) 1619fb551bf72929b316abb6d96cfb2ec05e896042aStefan Richter schedule_iso_resource(container_of(resource, 1629fb551bf72929b316abb6d96cfb2ec05e896042aStefan Richter struct iso_resource, resource), 0); 1639fb551bf72929b316abb6d96cfb2ec05e896042aStefan Richter} 1649fb551bf72929b316abb6d96cfb2ec05e896042aStefan Richter 16597c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter/* 16697c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter * dequeue_event() just kfree()'s the event, so the event has to be 16797c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter * the first field in a struct XYZ_event. 16897c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter */ 16997c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richterstruct event { 17097c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter struct { void *data; size_t size; } v[2]; 17197c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter struct list_head link; 17297c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter}; 17397c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter 17497c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richterstruct bus_reset_event { 17597c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter struct event event; 17697c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter struct fw_cdev_event_bus_reset reset; 17797c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter}; 17897c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter 17997c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richterstruct outbound_transaction_event { 18097c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter struct event event; 18197c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter struct client *client; 18297c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter struct outbound_transaction_resource r; 18397c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter struct fw_cdev_event_response response; 18497c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter}; 18597c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter 18697c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richterstruct inbound_transaction_event { 18797c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter struct event event; 188e205597d188a9ea69ce43f740a14f07b3f5b996aStefan Richter union { 189e205597d188a9ea69ce43f740a14f07b3f5b996aStefan Richter struct fw_cdev_event_request request; 190e205597d188a9ea69ce43f740a14f07b3f5b996aStefan Richter struct fw_cdev_event_request2 request2; 191e205597d188a9ea69ce43f740a14f07b3f5b996aStefan Richter } req; 19297c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter}; 19397c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter 19497c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richterstruct iso_interrupt_event { 19597c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter struct event event; 19697c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter struct fw_cdev_event_iso_interrupt interrupt; 19797c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter}; 19897c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter 199872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richterstruct iso_interrupt_mc_event { 200872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter struct event event; 201872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter struct fw_cdev_event_iso_interrupt_mc interrupt; 202872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter}; 203872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter 204b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richterstruct iso_resource_event { 205b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter struct event event; 206e21fcf798e246202d7b60e864f1d7302ebaaf41cStefan Richter struct fw_cdev_event_iso_resource iso_resource; 207b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter}; 208b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter 209850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richterstruct outbound_phy_packet_event { 210850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter struct event event; 211850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter struct client *client; 212850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter struct fw_packet p; 213850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter struct fw_cdev_event_phy_packet phy_packet; 214850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter}; 215850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter 216bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richterstruct inbound_phy_packet_event { 217bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter struct event event; 218bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter struct fw_cdev_event_phy_packet phy_packet; 219bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter}; 220bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter 2219c1176b6a28850703ea6e3a0f0c703f6d6c61cd3Stefan Richter#ifdef CONFIG_COMPAT 2229c1176b6a28850703ea6e3a0f0c703f6d6c61cd3Stefan Richterstatic void __user *u64_to_uptr(u64 value) 2239c1176b6a28850703ea6e3a0f0c703f6d6c61cd3Stefan Richter{ 2249c1176b6a28850703ea6e3a0f0c703f6d6c61cd3Stefan Richter if (is_compat_task()) 2259c1176b6a28850703ea6e3a0f0c703f6d6c61cd3Stefan Richter return compat_ptr(value); 2269c1176b6a28850703ea6e3a0f0c703f6d6c61cd3Stefan Richter else 2279c1176b6a28850703ea6e3a0f0c703f6d6c61cd3Stefan Richter return (void __user *)(unsigned long)value; 2289c1176b6a28850703ea6e3a0f0c703f6d6c61cd3Stefan Richter} 2299c1176b6a28850703ea6e3a0f0c703f6d6c61cd3Stefan Richter 2309c1176b6a28850703ea6e3a0f0c703f6d6c61cd3Stefan Richterstatic u64 uptr_to_u64(void __user *ptr) 2319c1176b6a28850703ea6e3a0f0c703f6d6c61cd3Stefan Richter{ 2329c1176b6a28850703ea6e3a0f0c703f6d6c61cd3Stefan Richter if (is_compat_task()) 2339c1176b6a28850703ea6e3a0f0c703f6d6c61cd3Stefan Richter return ptr_to_compat(ptr); 2349c1176b6a28850703ea6e3a0f0c703f6d6c61cd3Stefan Richter else 2359c1176b6a28850703ea6e3a0f0c703f6d6c61cd3Stefan Richter return (u64)(unsigned long)ptr; 2369c1176b6a28850703ea6e3a0f0c703f6d6c61cd3Stefan Richter} 2379c1176b6a28850703ea6e3a0f0c703f6d6c61cd3Stefan Richter#else 2389c1176b6a28850703ea6e3a0f0c703f6d6c61cd3Stefan Richterstatic inline void __user *u64_to_uptr(u64 value) 23919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{ 24019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg return (void __user *)(unsigned long)value; 24119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg} 24219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 2439c1176b6a28850703ea6e3a0f0c703f6d6c61cd3Stefan Richterstatic inline u64 uptr_to_u64(void __user *ptr) 24419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{ 2459c1176b6a28850703ea6e3a0f0c703f6d6c61cd3Stefan Richter return (u64)(unsigned long)ptr; 24619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg} 2479c1176b6a28850703ea6e3a0f0c703f6d6c61cd3Stefan Richter#endif /* CONFIG_COMPAT */ 24819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 24919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsbergstatic int fw_device_op_open(struct inode *inode, struct file *file) 25019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{ 25119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg struct fw_device *device; 25219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg struct client *client; 25319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 25496b19062e741b715cf399312c30e0672d8889569Stefan Richter device = fw_device_get_by_devt(inode->i_rdev); 255a3aca3dabbcf00f2088d472f27755c29acaa992eKristian Høgsberg if (device == NULL) 256a3aca3dabbcf00f2088d472f27755c29acaa992eKristian Høgsberg return -ENODEV; 25719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 258551f4cb9de716ffcdaf968c99a450c22ff12e8c3Jay Fenlason if (fw_device_is_shutdown(device)) { 259551f4cb9de716ffcdaf968c99a450c22ff12e8c3Jay Fenlason fw_device_put(device); 260551f4cb9de716ffcdaf968c99a450c22ff12e8c3Jay Fenlason return -ENODEV; 261551f4cb9de716ffcdaf968c99a450c22ff12e8c3Jay Fenlason } 262551f4cb9de716ffcdaf968c99a450c22ff12e8c3Jay Fenlason 2632d826cc5c791bdc5f5651324c485746be9492be0Kristian Høgsberg client = kzalloc(sizeof(*client), GFP_KERNEL); 26496b19062e741b715cf399312c30e0672d8889569Stefan Richter if (client == NULL) { 26596b19062e741b715cf399312c30e0672d8889569Stefan Richter fw_device_put(device); 26619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg return -ENOMEM; 26796b19062e741b715cf399312c30e0672d8889569Stefan Richter } 26819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 26996b19062e741b715cf399312c30e0672d8889569Stefan Richter client->device = device; 27019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg spin_lock_init(&client->lock); 27145ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason idr_init(&client->resource_idr); 27245ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason INIT_LIST_HEAD(&client->event_list); 27319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg init_waitqueue_head(&client->wait); 2745a5e62da9be255439e8ce59f96828775b7b33374Clemens Ladisch init_waitqueue_head(&client->tx_flush_wait); 275bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter INIT_LIST_HEAD(&client->phy_receiver_link); 27693b37905f70083d6143f5f4dba0a45cc64379a62Stefan Richter INIT_LIST_HEAD(&client->link); 277fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter kref_init(&client->kref); 27819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 27919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg file->private_data = client; 28019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 2813ac26b2ee30005930117fe6a180c139c5f300fafStefan Richter return nonseekable_open(inode, file); 28219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg} 28319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 28419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsbergstatic void queue_event(struct client *client, struct event *event, 28519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg void *data0, size_t size0, void *data1, size_t size1) 28619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{ 28719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg unsigned long flags; 28819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 28919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg event->v[0].data = data0; 29019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg event->v[0].size = size0; 29119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg event->v[1].data = data1; 29219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg event->v[1].size = size1; 29319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 29419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg spin_lock_irqsave(&client->lock, flags); 29545ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason if (client->in_shutdown) 29645ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason kfree(event); 29745ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason else 29845ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason list_add_tail(&event->link, &client->event_list); 29919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg spin_unlock_irqrestore(&client->lock, flags); 30083431cba3d847fc2296d3f38ce7feb623a1cfc45Jay Fenlason 30183431cba3d847fc2296d3f38ce7feb623a1cfc45Jay Fenlason wake_up_interruptible(&client->wait); 30219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg} 30319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 30453dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richterstatic int dequeue_event(struct client *client, 30553dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richter char __user *buffer, size_t count) 30619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{ 30719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg struct event *event; 30819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg size_t size, total; 3092dbd7d7e2327b0c2cc4e2de903e1cfa19980a504Stefan Richter int i, ret; 31019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 3112dbd7d7e2327b0c2cc4e2de903e1cfa19980a504Stefan Richter ret = wait_event_interruptible(client->wait, 3122dbd7d7e2327b0c2cc4e2de903e1cfa19980a504Stefan Richter !list_empty(&client->event_list) || 3132dbd7d7e2327b0c2cc4e2de903e1cfa19980a504Stefan Richter fw_device_is_shutdown(client->device)); 3142dbd7d7e2327b0c2cc4e2de903e1cfa19980a504Stefan Richter if (ret < 0) 3152dbd7d7e2327b0c2cc4e2de903e1cfa19980a504Stefan Richter return ret; 31619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 3172603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg if (list_empty(&client->event_list) && 3182603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg fw_device_is_shutdown(client->device)) 3192603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg return -ENODEV; 32019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 3213ba949868a6dc082b24cba5c3bf3f50de7391433Stefan Richter spin_lock_irq(&client->lock); 322a459b8ab9c176143fecef8ace4b70d6dbd7a8113Stefan Richter event = list_first_entry(&client->event_list, struct event, link); 32319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg list_del(&event->link); 3243ba949868a6dc082b24cba5c3bf3f50de7391433Stefan Richter spin_unlock_irq(&client->lock); 32519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 32619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg total = 0; 32719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg for (i = 0; i < ARRAY_SIZE(event->v) && total < count; i++) { 32819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg size = min(event->v[i].size, count - total); 3292603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg if (copy_to_user(buffer + total, event->v[i].data, size)) { 3302dbd7d7e2327b0c2cc4e2de903e1cfa19980a504Stefan Richter ret = -EFAULT; 33119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg goto out; 3322603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg } 33319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg total += size; 33419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg } 3352dbd7d7e2327b0c2cc4e2de903e1cfa19980a504Stefan Richter ret = total; 33619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 33719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg out: 33819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg kfree(event); 33919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 3402dbd7d7e2327b0c2cc4e2de903e1cfa19980a504Stefan Richter return ret; 34119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg} 34219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 34353dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richterstatic ssize_t fw_device_op_read(struct file *file, char __user *buffer, 34453dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richter size_t count, loff_t *offset) 34519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{ 34619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg struct client *client = file->private_data; 34719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 34819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg return dequeue_event(client, buffer, count); 34919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg} 35019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 35153dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richterstatic void fill_bus_reset_event(struct fw_cdev_event_bus_reset *event, 35253dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richter struct client *client) 353344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg{ 354da8ecffaed434a12930f652898f9e86d1c2abc3eKristian Høgsberg struct fw_card *card = client->device->card; 355cf417e5494582453c033d8cac9e1352e74215435Jay Fenlason 3563ba949868a6dc082b24cba5c3bf3f50de7391433Stefan Richter spin_lock_irq(&card->lock); 357344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg 358da8ecffaed434a12930f652898f9e86d1c2abc3eKristian Høgsberg event->closure = client->bus_reset_closure; 359344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg event->type = FW_CDEV_EVENT_BUS_RESET; 360cf5a56ac8083dd04ffe8b9b2ec7895e9bcff44bcStefan Richter event->generation = client->device->generation; 361da8ecffaed434a12930f652898f9e86d1c2abc3eKristian Høgsberg event->node_id = client->device->node_id; 362344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg event->local_node_id = card->local_node->node_id; 363250b2b6dd421c9f8844a867d2ac06e0661e0ad93Stefan Richter event->bm_node_id = card->bm_node_id; 364344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg event->irm_node_id = card->irm_node->node_id; 365344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg event->root_node_id = card->root_node->node_id; 366cf417e5494582453c033d8cac9e1352e74215435Jay Fenlason 3673ba949868a6dc082b24cba5c3bf3f50de7391433Stefan Richter spin_unlock_irq(&card->lock); 368344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg} 369344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg 37053dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richterstatic void for_each_client(struct fw_device *device, 37153dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richter void (*callback)(struct client *client)) 3722603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg{ 3732603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg struct client *c; 3742603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg 375d67cfb9613f373d76daa2c8d209629601424ca12Stefan Richter mutex_lock(&device->client_list_mutex); 3762603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg list_for_each_entry(c, &device->client_list, link) 3772603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg callback(c); 378d67cfb9613f373d76daa2c8d209629601424ca12Stefan Richter mutex_unlock(&device->client_list_mutex); 3792603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg} 3802603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg 381b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richterstatic int schedule_reallocations(int id, void *p, void *data) 382b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter{ 3839fb551bf72929b316abb6d96cfb2ec05e896042aStefan Richter schedule_if_iso_resource(p); 384b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter 385b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter return 0; 386b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter} 387b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter 38853dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richterstatic void queue_bus_reset_event(struct client *client) 38997bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg{ 39097c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter struct bus_reset_event *e; 39197bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg 39297c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter e = kzalloc(sizeof(*e), GFP_KERNEL); 393cfb0c9d1ffbf930a4a852f178b161c522b21b0abStefan Richter if (e == NULL) 39497bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg return; 39597bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg 39697c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter fill_bus_reset_event(&e->reset, client); 39797bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg 39897c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter queue_event(client, &e->event, 39997c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter &e->reset, sizeof(e->reset), NULL, 0); 400b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter 401b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter spin_lock_irq(&client->lock); 402b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter idr_for_each(&client->resource_idr, schedule_reallocations, client); 403b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter spin_unlock_irq(&client->lock); 40497bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg} 40597bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg 40697bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsbergvoid fw_device_cdev_update(struct fw_device *device) 40797bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg{ 4082603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg for_each_client(device, queue_bus_reset_event); 4092603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg} 41097bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg 4112603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsbergstatic void wake_up_client(struct client *client) 4122603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg{ 4132603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg wake_up_interruptible(&client->wait); 4142603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg} 41597bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg 4162603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsbergvoid fw_device_cdev_remove(struct fw_device *device) 4172603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg{ 4182603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg for_each_client(device, wake_up_client); 41997bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg} 42097bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg 4216e95dea728f4af36c033fcf2318529bd46dae540Stefan Richterunion ioctl_arg { 4226e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter struct fw_cdev_get_info get_info; 4236e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter struct fw_cdev_send_request send_request; 4246e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter struct fw_cdev_allocate allocate; 4256e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter struct fw_cdev_deallocate deallocate; 4266e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter struct fw_cdev_send_response send_response; 4276e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter struct fw_cdev_initiate_bus_reset initiate_bus_reset; 4286e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter struct fw_cdev_add_descriptor add_descriptor; 4296e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter struct fw_cdev_remove_descriptor remove_descriptor; 4306e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter struct fw_cdev_create_iso_context create_iso_context; 4316e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter struct fw_cdev_queue_iso queue_iso; 4326e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter struct fw_cdev_start_iso start_iso; 4336e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter struct fw_cdev_stop_iso stop_iso; 4346e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter struct fw_cdev_get_cycle_timer get_cycle_timer; 4356e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter struct fw_cdev_allocate_iso_resource allocate_iso_resource; 4366e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter struct fw_cdev_send_stream_packet send_stream_packet; 4376e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter struct fw_cdev_get_cycle_timer2 get_cycle_timer2; 438850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter struct fw_cdev_send_phy_packet send_phy_packet; 439bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter struct fw_cdev_receive_phy_packets receive_phy_packets; 440872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter struct fw_cdev_set_iso_channels set_iso_channels; 441d1bbd20972936b9b178fda3eb1ec417cb27fdc01Clemens Ladisch struct fw_cdev_flush_iso flush_iso; 4426e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter}; 4436e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter 4446e95dea728f4af36c033fcf2318529bd46dae540Stefan Richterstatic int ioctl_get_info(struct client *client, union ioctl_arg *arg) 44519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{ 4466e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter struct fw_cdev_get_info *a = &arg->get_info; 447344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg struct fw_cdev_event_bus_reset bus_reset; 448c9755e14a01987ada4063e8b4c50c2b6738d879eStefan Richter unsigned long ret = 0; 449344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg 4506e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter client->version = a->version; 451604f45167824e18ad5766e51ecf1d4d65f15118dStefan Richter a->version = FW_CDEV_KERNEL_VERSION; 4526e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter a->card = client->device->card->index; 453344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg 454c9755e14a01987ada4063e8b4c50c2b6738d879eStefan Richter down_read(&fw_device_rwsem); 455c9755e14a01987ada4063e8b4c50c2b6738d879eStefan Richter 4566e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter if (a->rom != 0) { 4576e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter size_t want = a->rom_length; 458d84702a5d7b500ead8db129ddea789c88764f357Stefan Richter size_t have = client->device->config_rom_length * 4; 459344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg 4606e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter ret = copy_to_user(u64_to_uptr(a->rom), 4616e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter client->device->config_rom, min(want, have)); 462344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg } 4636e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter a->rom_length = client->device->config_rom_length * 4; 464344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg 465c9755e14a01987ada4063e8b4c50c2b6738d879eStefan Richter up_read(&fw_device_rwsem); 466c9755e14a01987ada4063e8b4c50c2b6738d879eStefan Richter 467c9755e14a01987ada4063e8b4c50c2b6738d879eStefan Richter if (ret != 0) 468c9755e14a01987ada4063e8b4c50c2b6738d879eStefan Richter return -EFAULT; 469c9755e14a01987ada4063e8b4c50c2b6738d879eStefan Richter 47093b37905f70083d6143f5f4dba0a45cc64379a62Stefan Richter mutex_lock(&client->device->client_list_mutex); 47193b37905f70083d6143f5f4dba0a45cc64379a62Stefan Richter 4726e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter client->bus_reset_closure = a->bus_reset_closure; 4736e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter if (a->bus_reset != 0) { 474da8ecffaed434a12930f652898f9e86d1c2abc3eKristian Høgsberg fill_bus_reset_event(&bus_reset, client); 475790198f74c9d1b46b6a89504361b1a844670d050Stefan Richter /* unaligned size of bus_reset is 36 bytes */ 476790198f74c9d1b46b6a89504361b1a844670d050Stefan Richter ret = copy_to_user(u64_to_uptr(a->bus_reset), &bus_reset, 36); 477344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg } 47893b37905f70083d6143f5f4dba0a45cc64379a62Stefan Richter if (ret == 0 && list_empty(&client->link)) 47993b37905f70083d6143f5f4dba0a45cc64379a62Stefan Richter list_add_tail(&client->link, &client->device->client_list); 48019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 48193b37905f70083d6143f5f4dba0a45cc64379a62Stefan Richter mutex_unlock(&client->device->client_list_mutex); 48293b37905f70083d6143f5f4dba0a45cc64379a62Stefan Richter 48393b37905f70083d6143f5f4dba0a45cc64379a62Stefan Richter return ret ? -EFAULT : 0; 48419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg} 48519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 48653dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richterstatic int add_client_resource(struct client *client, 48753dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richter struct client_resource *resource, gfp_t gfp_mask) 4883964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg{ 4890a41981803fcd4107fff4e943afb72940ba653d2Stefan Richter bool preload = !!(gfp_mask & __GFP_WAIT); 4903964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg unsigned long flags; 49145ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason int ret; 49245ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason 49337b61890d757f606c25ac5a247572cb7d5efde96Tejun Heo if (preload) 49437b61890d757f606c25ac5a247572cb7d5efde96Tejun Heo idr_preload(gfp_mask); 4953964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg spin_lock_irqsave(&client->lock, flags); 49637b61890d757f606c25ac5a247572cb7d5efde96Tejun Heo 49745ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason if (client->in_shutdown) 49845ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason ret = -ECANCELED; 49945ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason else 50037b61890d757f606c25ac5a247572cb7d5efde96Tejun Heo ret = idr_alloc(&client->resource_idr, resource, 0, 0, 50137b61890d757f606c25ac5a247572cb7d5efde96Tejun Heo GFP_NOWAIT); 502b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter if (ret >= 0) { 50337b61890d757f606c25ac5a247572cb7d5efde96Tejun Heo resource->handle = ret; 504fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter client_get(client); 5059fb551bf72929b316abb6d96cfb2ec05e896042aStefan Richter schedule_if_iso_resource(resource); 506b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter } 50745ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason 50837b61890d757f606c25ac5a247572cb7d5efde96Tejun Heo spin_unlock_irqrestore(&client->lock, flags); 50937b61890d757f606c25ac5a247572cb7d5efde96Tejun Heo if (preload) 51037b61890d757f606c25ac5a247572cb7d5efde96Tejun Heo idr_preload_end(); 51145ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason 51245ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason return ret < 0 ? ret : 0; 5133964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg} 5143964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg 51553dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richterstatic int release_client_resource(struct client *client, u32 handle, 51653dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richter client_resource_release_fn_t release, 517e21fcf798e246202d7b60e864f1d7302ebaaf41cStefan Richter struct client_resource **return_resource) 5183964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg{ 519e21fcf798e246202d7b60e864f1d7302ebaaf41cStefan Richter struct client_resource *resource; 5203964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg 5213ba949868a6dc082b24cba5c3bf3f50de7391433Stefan Richter spin_lock_irq(&client->lock); 52245ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason if (client->in_shutdown) 523e21fcf798e246202d7b60e864f1d7302ebaaf41cStefan Richter resource = NULL; 52445ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason else 525e21fcf798e246202d7b60e864f1d7302ebaaf41cStefan Richter resource = idr_find(&client->resource_idr, handle); 526e21fcf798e246202d7b60e864f1d7302ebaaf41cStefan Richter if (resource && resource->release == release) 52745ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason idr_remove(&client->resource_idr, handle); 5283ba949868a6dc082b24cba5c3bf3f50de7391433Stefan Richter spin_unlock_irq(&client->lock); 5293964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg 530e21fcf798e246202d7b60e864f1d7302ebaaf41cStefan Richter if (!(resource && resource->release == release)) 5313964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg return -EINVAL; 5323964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg 533e21fcf798e246202d7b60e864f1d7302ebaaf41cStefan Richter if (return_resource) 534e21fcf798e246202d7b60e864f1d7302ebaaf41cStefan Richter *return_resource = resource; 5353964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg else 536e21fcf798e246202d7b60e864f1d7302ebaaf41cStefan Richter resource->release(client, resource); 5373964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg 538fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter client_put(client); 539fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter 5403964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg return 0; 5413964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg} 5423964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg 54353dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richterstatic void release_transaction(struct client *client, 54453dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richter struct client_resource *resource) 5453964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg{ 5463964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg} 5473964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg 54853dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richterstatic void complete_transaction(struct fw_card *card, int rcode, 54953dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richter void *payload, size_t length, void *data) 55019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{ 55197c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter struct outbound_transaction_event *e = data; 55297c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter struct fw_cdev_event_response *rsp = &e->response; 55397c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter struct client *client = e->client; 55428cf6a04c82857d562968dc3a8a89726e6ac3dcbKristian Høgsberg unsigned long flags; 55519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 55697c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter if (length < rsp->length) 55797c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter rsp->length = length; 55819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg if (rcode == RCODE_COMPLETE) 55997c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter memcpy(rsp->data, payload, rsp->length); 56019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 56128cf6a04c82857d562968dc3a8a89726e6ac3dcbKristian Høgsberg spin_lock_irqsave(&client->lock, flags); 5625a5e62da9be255439e8ce59f96828775b7b33374Clemens Ladisch idr_remove(&client->resource_idr, e->r.resource.handle); 5635a5e62da9be255439e8ce59f96828775b7b33374Clemens Ladisch if (client->in_shutdown) 5645a5e62da9be255439e8ce59f96828775b7b33374Clemens Ladisch wake_up(&client->tx_flush_wait); 56528cf6a04c82857d562968dc3a8a89726e6ac3dcbKristian Høgsberg spin_unlock_irqrestore(&client->lock, flags); 56628cf6a04c82857d562968dc3a8a89726e6ac3dcbKristian Høgsberg 56797c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter rsp->type = FW_CDEV_EVENT_RESPONSE; 56897c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter rsp->rcode = rcode; 5698401d92ba46a1e859464cbd9c9ee304f6e361da3David Moore 5708401d92ba46a1e859464cbd9c9ee304f6e361da3David Moore /* 57197c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter * In the case that sizeof(*rsp) doesn't align with the position of the 5728401d92ba46a1e859464cbd9c9ee304f6e361da3David Moore * data, and the read is short, preserve an extra copy of the data 5738401d92ba46a1e859464cbd9c9ee304f6e361da3David Moore * to stay compatible with a pre-2.6.27 bug. Since the bug is harmless 5748401d92ba46a1e859464cbd9c9ee304f6e361da3David Moore * for short reads and some apps depended on it, this is both safe 5758401d92ba46a1e859464cbd9c9ee304f6e361da3David Moore * and prudent for compatibility. 5768401d92ba46a1e859464cbd9c9ee304f6e361da3David Moore */ 57797c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter if (rsp->length <= sizeof(*rsp) - offsetof(typeof(*rsp), data)) 57897c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter queue_event(client, &e->event, rsp, sizeof(*rsp), 57997c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter rsp->data, rsp->length); 5808401d92ba46a1e859464cbd9c9ee304f6e361da3David Moore else 58197c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter queue_event(client, &e->event, rsp, sizeof(*rsp) + rsp->length, 5828401d92ba46a1e859464cbd9c9ee304f6e361da3David Moore NULL, 0); 583fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter 5845a5e62da9be255439e8ce59f96828775b7b33374Clemens Ladisch /* Drop the idr's reference */ 5855a5e62da9be255439e8ce59f96828775b7b33374Clemens Ladisch client_put(client); 58619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg} 58719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 588acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richterstatic int init_request(struct client *client, 589acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter struct fw_cdev_send_request *request, 590acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter int destination_id, int speed) 59119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{ 59297c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter struct outbound_transaction_event *e; 5931f3125af8ed7410cc0ebcc0acd59bbfc1ae0057aStefan Richter int ret; 59419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 59518e9b10fcdc090d3a38606958167d5923c7099b7Stefan Richter if (request->tcode != TCODE_STREAM_DATA && 59618e9b10fcdc090d3a38606958167d5923c7099b7Stefan Richter (request->length > 4096 || request->length > 512 << speed)) 5975d3fd692a7196a9045fb606f891f5987959b65a0Stefan Richter return -EIO; 59819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 599a8e93f3dccc066cd6dd1e9db1e35942914fc57d1Clemens Ladisch if (request->tcode == TCODE_WRITE_QUADLET_REQUEST && 600a8e93f3dccc066cd6dd1e9db1e35942914fc57d1Clemens Ladisch request->length < 4) 601a8e93f3dccc066cd6dd1e9db1e35942914fc57d1Clemens Ladisch return -EINVAL; 602a8e93f3dccc066cd6dd1e9db1e35942914fc57d1Clemens Ladisch 60397c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter e = kmalloc(sizeof(*e) + request->length, GFP_KERNEL); 60497c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter if (e == NULL) 60519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg return -ENOMEM; 60619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 60797c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter e->client = client; 60897c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter e->response.length = request->length; 60997c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter e->response.closure = request->closure; 61019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 6114f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg if (request->data && 61297c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter copy_from_user(e->response.data, 6134f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg u64_to_uptr(request->data), request->length)) { 6141f3125af8ed7410cc0ebcc0acd59bbfc1ae0057aStefan Richter ret = -EFAULT; 61545ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason goto failed; 6161f3125af8ed7410cc0ebcc0acd59bbfc1ae0057aStefan Richter } 6171f3125af8ed7410cc0ebcc0acd59bbfc1ae0057aStefan Richter 61897c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter e->r.resource.release = release_transaction; 61997c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter ret = add_client_resource(client, &e->r.resource, GFP_KERNEL); 62045ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason if (ret < 0) 62145ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason goto failed; 62228cf6a04c82857d562968dc3a8a89726e6ac3dcbKristian Høgsberg 623acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter fw_send_request(client->device->card, &e->r.transaction, 624664d8010b170ae8b3ce9268b4f4da934d27b0491Stefan Richter request->tcode, destination_id, request->generation, 625664d8010b170ae8b3ce9268b4f4da934d27b0491Stefan Richter speed, request->offset, e->response.data, 626664d8010b170ae8b3ce9268b4f4da934d27b0491Stefan Richter request->length, complete_transaction, e); 627664d8010b170ae8b3ce9268b4f4da934d27b0491Stefan Richter return 0; 62819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 62945ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason failed: 63097c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter kfree(e); 6311f3125af8ed7410cc0ebcc0acd59bbfc1ae0057aStefan Richter 6321f3125af8ed7410cc0ebcc0acd59bbfc1ae0057aStefan Richter return ret; 63319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg} 63419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 6356e95dea728f4af36c033fcf2318529bd46dae540Stefan Richterstatic int ioctl_send_request(struct client *client, union ioctl_arg *arg) 636acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter{ 6376e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter switch (arg->send_request.tcode) { 638acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter case TCODE_WRITE_QUADLET_REQUEST: 639acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter case TCODE_WRITE_BLOCK_REQUEST: 640acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter case TCODE_READ_QUADLET_REQUEST: 641acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter case TCODE_READ_BLOCK_REQUEST: 642acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter case TCODE_LOCK_MASK_SWAP: 643acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter case TCODE_LOCK_COMPARE_SWAP: 644acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter case TCODE_LOCK_FETCH_ADD: 645acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter case TCODE_LOCK_LITTLE_ADD: 646acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter case TCODE_LOCK_BOUNDED_ADD: 647acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter case TCODE_LOCK_WRAP_ADD: 648acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter case TCODE_LOCK_VENDOR_DEPENDENT: 649acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter break; 650acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter default: 651acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter return -EINVAL; 652acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter } 653acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter 6546e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter return init_request(client, &arg->send_request, client->device->node_id, 655acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter client->device->max_speed); 656acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter} 657acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter 658281e20323ab72180137824a298ee9e21e6f9acf6Stefan Richterstatic inline bool is_fcp_request(struct fw_request *request) 659281e20323ab72180137824a298ee9e21e6f9acf6Stefan Richter{ 660281e20323ab72180137824a298ee9e21e6f9acf6Stefan Richter return request == NULL; 661281e20323ab72180137824a298ee9e21e6f9acf6Stefan Richter} 662281e20323ab72180137824a298ee9e21e6f9acf6Stefan Richter 66353dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richterstatic void release_request(struct client *client, 66453dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richter struct client_resource *resource) 6653964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg{ 66697c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter struct inbound_transaction_resource *r = container_of(resource, 66797c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter struct inbound_transaction_resource, resource); 6683964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg 669281e20323ab72180137824a298ee9e21e6f9acf6Stefan Richter if (is_fcp_request(r->request)) 670281e20323ab72180137824a298ee9e21e6f9acf6Stefan Richter kfree(r->data); 671281e20323ab72180137824a298ee9e21e6f9acf6Stefan Richter else 67208bd34c98d631fe85744d4c920c80f48a1d95f54Jay Fenlason fw_send_response(r->card, r->request, RCODE_CONFLICT_ERROR); 6730244f57302f7e8bebd2f1ab58767eac2e9f678a6Stefan Richter 6740244f57302f7e8bebd2f1ab58767eac2e9f678a6Stefan Richter fw_card_put(r->card); 67597c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter kfree(r); 6763964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg} 6773964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg 67897c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richterstatic void handle_request(struct fw_card *card, struct fw_request *request, 67953dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richter int tcode, int destination, int source, 68033e553fe2b4a983ef34a57ab1440d8d33397bb12Stefan Richter int generation, unsigned long long offset, 68153dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richter void *payload, size_t length, void *callback_data) 68219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{ 68397c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter struct address_handler_resource *handler = callback_data; 68497c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter struct inbound_transaction_resource *r; 68597c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter struct inbound_transaction_event *e; 686e205597d188a9ea69ce43f740a14f07b3f5b996aStefan Richter size_t event_size0; 687281e20323ab72180137824a298ee9e21e6f9acf6Stefan Richter void *fcp_frame = NULL; 68845ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason int ret; 68919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 6900244f57302f7e8bebd2f1ab58767eac2e9f678a6Stefan Richter /* card may be different from handler->client->device->card */ 6910244f57302f7e8bebd2f1ab58767eac2e9f678a6Stefan Richter fw_card_get(card); 6920244f57302f7e8bebd2f1ab58767eac2e9f678a6Stefan Richter 69397c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter r = kmalloc(sizeof(*r), GFP_ATOMIC); 6942d826cc5c791bdc5f5651324c485746be9492be0Kristian Høgsberg e = kmalloc(sizeof(*e), GFP_ATOMIC); 695cfb0c9d1ffbf930a4a852f178b161c522b21b0abStefan Richter if (r == NULL || e == NULL) 69645ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason goto failed; 697cfb0c9d1ffbf930a4a852f178b161c522b21b0abStefan Richter 69808bd34c98d631fe85744d4c920c80f48a1d95f54Jay Fenlason r->card = card; 69997c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter r->request = request; 70097c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter r->data = payload; 70197c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter r->length = length; 70219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 703281e20323ab72180137824a298ee9e21e6f9acf6Stefan Richter if (is_fcp_request(request)) { 704281e20323ab72180137824a298ee9e21e6f9acf6Stefan Richter /* 705281e20323ab72180137824a298ee9e21e6f9acf6Stefan Richter * FIXME: Let core-transaction.c manage a 706281e20323ab72180137824a298ee9e21e6f9acf6Stefan Richter * single reference-counted copy? 707281e20323ab72180137824a298ee9e21e6f9acf6Stefan Richter */ 708281e20323ab72180137824a298ee9e21e6f9acf6Stefan Richter fcp_frame = kmemdup(payload, length, GFP_ATOMIC); 709281e20323ab72180137824a298ee9e21e6f9acf6Stefan Richter if (fcp_frame == NULL) 710281e20323ab72180137824a298ee9e21e6f9acf6Stefan Richter goto failed; 711281e20323ab72180137824a298ee9e21e6f9acf6Stefan Richter 712281e20323ab72180137824a298ee9e21e6f9acf6Stefan Richter r->data = fcp_frame; 713281e20323ab72180137824a298ee9e21e6f9acf6Stefan Richter } 714281e20323ab72180137824a298ee9e21e6f9acf6Stefan Richter 71597c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter r->resource.release = release_request; 71697c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter ret = add_client_resource(handler->client, &r->resource, GFP_ATOMIC); 71745ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason if (ret < 0) 71845ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason goto failed; 71919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 720e205597d188a9ea69ce43f740a14f07b3f5b996aStefan Richter if (handler->client->version < FW_CDEV_VERSION_EVENT_REQUEST2) { 721e205597d188a9ea69ce43f740a14f07b3f5b996aStefan Richter struct fw_cdev_event_request *req = &e->req.request; 722e205597d188a9ea69ce43f740a14f07b3f5b996aStefan Richter 723e205597d188a9ea69ce43f740a14f07b3f5b996aStefan Richter if (tcode & 0x10) 724e205597d188a9ea69ce43f740a14f07b3f5b996aStefan Richter tcode = TCODE_LOCK_REQUEST; 725e205597d188a9ea69ce43f740a14f07b3f5b996aStefan Richter 726e205597d188a9ea69ce43f740a14f07b3f5b996aStefan Richter req->type = FW_CDEV_EVENT_REQUEST; 727e205597d188a9ea69ce43f740a14f07b3f5b996aStefan Richter req->tcode = tcode; 728e205597d188a9ea69ce43f740a14f07b3f5b996aStefan Richter req->offset = offset; 729e205597d188a9ea69ce43f740a14f07b3f5b996aStefan Richter req->length = length; 730e205597d188a9ea69ce43f740a14f07b3f5b996aStefan Richter req->handle = r->resource.handle; 731e205597d188a9ea69ce43f740a14f07b3f5b996aStefan Richter req->closure = handler->closure; 732e205597d188a9ea69ce43f740a14f07b3f5b996aStefan Richter event_size0 = sizeof(*req); 733e205597d188a9ea69ce43f740a14f07b3f5b996aStefan Richter } else { 734e205597d188a9ea69ce43f740a14f07b3f5b996aStefan Richter struct fw_cdev_event_request2 *req = &e->req.request2; 735e205597d188a9ea69ce43f740a14f07b3f5b996aStefan Richter 736e205597d188a9ea69ce43f740a14f07b3f5b996aStefan Richter req->type = FW_CDEV_EVENT_REQUEST2; 737e205597d188a9ea69ce43f740a14f07b3f5b996aStefan Richter req->tcode = tcode; 738e205597d188a9ea69ce43f740a14f07b3f5b996aStefan Richter req->offset = offset; 739e205597d188a9ea69ce43f740a14f07b3f5b996aStefan Richter req->source_node_id = source; 740e205597d188a9ea69ce43f740a14f07b3f5b996aStefan Richter req->destination_node_id = destination; 741e205597d188a9ea69ce43f740a14f07b3f5b996aStefan Richter req->card = card->index; 742e205597d188a9ea69ce43f740a14f07b3f5b996aStefan Richter req->generation = generation; 743e205597d188a9ea69ce43f740a14f07b3f5b996aStefan Richter req->length = length; 744e205597d188a9ea69ce43f740a14f07b3f5b996aStefan Richter req->handle = r->resource.handle; 745e205597d188a9ea69ce43f740a14f07b3f5b996aStefan Richter req->closure = handler->closure; 746e205597d188a9ea69ce43f740a14f07b3f5b996aStefan Richter event_size0 = sizeof(*req); 747e205597d188a9ea69ce43f740a14f07b3f5b996aStefan Richter } 74819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 74997c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter queue_event(handler->client, &e->event, 750e205597d188a9ea69ce43f740a14f07b3f5b996aStefan Richter &e->req, event_size0, r->data, length); 75145ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason return; 75245ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason 75345ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason failed: 75497c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter kfree(r); 75545ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason kfree(e); 756281e20323ab72180137824a298ee9e21e6f9acf6Stefan Richter kfree(fcp_frame); 757281e20323ab72180137824a298ee9e21e6f9acf6Stefan Richter 758281e20323ab72180137824a298ee9e21e6f9acf6Stefan Richter if (!is_fcp_request(request)) 759db5d247ae811f49185a71e703b65acad845e4b18Clemens Ladisch fw_send_response(card, request, RCODE_CONFLICT_ERROR); 7600244f57302f7e8bebd2f1ab58767eac2e9f678a6Stefan Richter 7610244f57302f7e8bebd2f1ab58767eac2e9f678a6Stefan Richter fw_card_put(card); 76219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg} 76319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 76453dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richterstatic void release_address_handler(struct client *client, 76553dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richter struct client_resource *resource) 7663964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg{ 76797c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter struct address_handler_resource *r = 76897c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter container_of(resource, struct address_handler_resource, resource); 7693964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg 77097c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter fw_core_remove_address_handler(&r->handler); 77197c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter kfree(r); 7723964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg} 7733964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg 7746e95dea728f4af36c033fcf2318529bd46dae540Stefan Richterstatic int ioctl_allocate(struct client *client, union ioctl_arg *arg) 77519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{ 7766e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter struct fw_cdev_allocate *a = &arg->allocate; 77797c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter struct address_handler_resource *r; 77819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg struct fw_address_region region; 77945ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason int ret; 78019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 78197c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter r = kmalloc(sizeof(*r), GFP_KERNEL); 78297c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter if (r == NULL) 78319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg return -ENOMEM; 78419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 7856e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter region.start = a->offset; 7868e2b2b46ea4ca5ef790dddf78b360ed736a62d7cStefan Richter if (client->version < FW_CDEV_VERSION_ALLOCATE_REGION_END) 7878e2b2b46ea4ca5ef790dddf78b360ed736a62d7cStefan Richter region.end = a->offset + a->length; 7888e2b2b46ea4ca5ef790dddf78b360ed736a62d7cStefan Richter else 7898e2b2b46ea4ca5ef790dddf78b360ed736a62d7cStefan Richter region.end = a->region_end; 7908e2b2b46ea4ca5ef790dddf78b360ed736a62d7cStefan Richter 7916e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter r->handler.length = a->length; 79297c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter r->handler.address_callback = handle_request; 7936e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter r->handler.callback_data = r; 7946e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter r->closure = a->closure; 7956e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter r->client = client; 79619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 79797c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter ret = fw_core_add_address_handler(&r->handler, ®ion); 7983e0b5f0d7cb5fef402517e41eebff5a0f0e65a13Stefan Richter if (ret < 0) { 79997c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter kfree(r); 8003e0b5f0d7cb5fef402517e41eebff5a0f0e65a13Stefan Richter return ret; 80119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg } 8028e2b2b46ea4ca5ef790dddf78b360ed736a62d7cStefan Richter a->offset = r->handler.offset; 80319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 80497c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter r->resource.release = release_address_handler; 80597c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter ret = add_client_resource(client, &r->resource, GFP_KERNEL); 80645ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason if (ret < 0) { 80797c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter release_address_handler(client, &r->resource); 80845ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason return ret; 80945ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason } 8106e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter a->handle = r->resource.handle; 81119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 81219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg return 0; 81319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg} 81419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 8156e95dea728f4af36c033fcf2318529bd46dae540Stefan Richterstatic int ioctl_deallocate(struct client *client, union ioctl_arg *arg) 8169472316b6eab3500ded544f6e86700c33541ef4eKristian Høgsberg{ 8176e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter return release_client_resource(client, arg->deallocate.handle, 81845ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason release_address_handler, NULL); 8199472316b6eab3500ded544f6e86700c33541ef4eKristian Høgsberg} 8209472316b6eab3500ded544f6e86700c33541ef4eKristian Høgsberg 8216e95dea728f4af36c033fcf2318529bd46dae540Stefan Richterstatic int ioctl_send_response(struct client *client, union ioctl_arg *arg) 82219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{ 8236e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter struct fw_cdev_send_response *a = &arg->send_response; 8243964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg struct client_resource *resource; 82597c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter struct inbound_transaction_resource *r; 8267e44c0b56b07a5e34de9943cfb2fee72e71a9f0eStefan Richter int ret = 0; 82719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 8286e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter if (release_client_resource(client, a->handle, 82945ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason release_request, &resource) < 0) 83019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg return -EINVAL; 83145ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason 83297c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter r = container_of(resource, struct inbound_transaction_resource, 83397c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter resource); 834281e20323ab72180137824a298ee9e21e6f9acf6Stefan Richter if (is_fcp_request(r->request)) 835281e20323ab72180137824a298ee9e21e6f9acf6Stefan Richter goto out; 836281e20323ab72180137824a298ee9e21e6f9acf6Stefan Richter 837a10c0ce76098857b899505d05de9f2e13ddf7a7aClemens Ladisch if (a->length != fw_get_response_length(r->request)) { 838a10c0ce76098857b899505d05de9f2e13ddf7a7aClemens Ladisch ret = -EINVAL; 839a10c0ce76098857b899505d05de9f2e13ddf7a7aClemens Ladisch kfree(r->request); 840a10c0ce76098857b899505d05de9f2e13ddf7a7aClemens Ladisch goto out; 841a10c0ce76098857b899505d05de9f2e13ddf7a7aClemens Ladisch } 842a10c0ce76098857b899505d05de9f2e13ddf7a7aClemens Ladisch if (copy_from_user(r->data, u64_to_uptr(a->data), a->length)) { 843281e20323ab72180137824a298ee9e21e6f9acf6Stefan Richter ret = -EFAULT; 844281e20323ab72180137824a298ee9e21e6f9acf6Stefan Richter kfree(r->request); 845281e20323ab72180137824a298ee9e21e6f9acf6Stefan Richter goto out; 8467e44c0b56b07a5e34de9943cfb2fee72e71a9f0eStefan Richter } 84708bd34c98d631fe85744d4c920c80f48a1d95f54Jay Fenlason fw_send_response(r->card, r->request, a->rcode); 8487e44c0b56b07a5e34de9943cfb2fee72e71a9f0eStefan Richter out: 8490244f57302f7e8bebd2f1ab58767eac2e9f678a6Stefan Richter fw_card_put(r->card); 85019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg kfree(r); 85119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 8527e44c0b56b07a5e34de9943cfb2fee72e71a9f0eStefan Richter return ret; 85319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg} 85419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 8556e95dea728f4af36c033fcf2318529bd46dae540Stefan Richterstatic int ioctl_initiate_bus_reset(struct client *client, union ioctl_arg *arg) 8565371842b723dd04df57171f2c74660966901380cKristian Høgsberg{ 85702d37bed188c500ee7afb0a2dc6b65a80704c58eStefan Richter fw_schedule_bus_reset(client->device->card, true, 8586e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter arg->initiate_bus_reset.type == FW_CDEV_SHORT_RESET); 85902d37bed188c500ee7afb0a2dc6b65a80704c58eStefan Richter return 0; 8605371842b723dd04df57171f2c74660966901380cKristian Høgsberg} 8615371842b723dd04df57171f2c74660966901380cKristian Høgsberg 8623964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsbergstatic void release_descriptor(struct client *client, 8633964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg struct client_resource *resource) 8643964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg{ 86597c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter struct descriptor_resource *r = 86697c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter container_of(resource, struct descriptor_resource, resource); 8673964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg 86897c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter fw_core_remove_descriptor(&r->descriptor); 86997c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter kfree(r); 8703964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg} 8713964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg 8726e95dea728f4af36c033fcf2318529bd46dae540Stefan Richterstatic int ioctl_add_descriptor(struct client *client, union ioctl_arg *arg) 87366dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg{ 8746e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter struct fw_cdev_add_descriptor *a = &arg->add_descriptor; 87597c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter struct descriptor_resource *r; 87645ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason int ret; 87766dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg 878de487da8ca5839d057e1f4b57ee3f387e180b800Stefan Richter /* Access policy: Allow this ioctl only on local nodes' device files. */ 87992368890d551794ee8d7e90477d8498bb7f82a9bStefan Richter if (!client->device->is_local) 880de487da8ca5839d057e1f4b57ee3f387e180b800Stefan Richter return -ENOSYS; 881de487da8ca5839d057e1f4b57ee3f387e180b800Stefan Richter 8826e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter if (a->length > 256) 88366dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg return -EINVAL; 88466dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg 8856e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter r = kmalloc(sizeof(*r) + a->length * 4, GFP_KERNEL); 88697c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter if (r == NULL) 88766dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg return -ENOMEM; 88866dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg 8896e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter if (copy_from_user(r->data, u64_to_uptr(a->data), a->length * 4)) { 89045ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason ret = -EFAULT; 89145ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason goto failed; 89266dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg } 89366dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg 8946e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter r->descriptor.length = a->length; 8956e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter r->descriptor.immediate = a->immediate; 8966e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter r->descriptor.key = a->key; 89797c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter r->descriptor.data = r->data; 89866dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg 89997c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter ret = fw_core_add_descriptor(&r->descriptor); 90045ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason if (ret < 0) 90145ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason goto failed; 90266dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg 90397c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter r->resource.release = release_descriptor; 90497c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter ret = add_client_resource(client, &r->resource, GFP_KERNEL); 90545ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason if (ret < 0) { 90697c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter fw_core_remove_descriptor(&r->descriptor); 90745ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason goto failed; 90845ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason } 9096e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter a->handle = r->resource.handle; 91066dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg 91166dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg return 0; 91245ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason failed: 91397c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter kfree(r); 91445ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason 91545ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason return ret; 91666dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg} 91766dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg 9186e95dea728f4af36c033fcf2318529bd46dae540Stefan Richterstatic int ioctl_remove_descriptor(struct client *client, union ioctl_arg *arg) 91966dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg{ 9206e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter return release_client_resource(client, arg->remove_descriptor.handle, 92145ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason release_descriptor, NULL); 92266dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg} 92366dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg 92453dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richterstatic void iso_callback(struct fw_iso_context *context, u32 cycle, 92553dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richter size_t header_length, void *header, void *data) 92619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{ 92719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg struct client *client = data; 92897c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter struct iso_interrupt_event *e; 92919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 93056d04cb189f955e5167c27944d61aa57ad69b598Stefan Richter e = kmalloc(sizeof(*e) + header_length, GFP_ATOMIC); 931cfb0c9d1ffbf930a4a852f178b161c522b21b0abStefan Richter if (e == NULL) 93219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg return; 933cfb0c9d1ffbf930a4a852f178b161c522b21b0abStefan Richter 93497c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter e->interrupt.type = FW_CDEV_EVENT_ISO_INTERRUPT; 93597c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter e->interrupt.closure = client->iso_closure; 93697c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter e->interrupt.cycle = cycle; 93797c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter e->interrupt.header_length = header_length; 93897c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter memcpy(e->interrupt.header, header, header_length); 93997c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter queue_event(client, &e->event, &e->interrupt, 94097c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter sizeof(e->interrupt) + header_length, NULL, 0); 94119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg} 94219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 943872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richterstatic void iso_mc_callback(struct fw_iso_context *context, 944872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter dma_addr_t completed, void *data) 945872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter{ 946872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter struct client *client = data; 947872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter struct iso_interrupt_mc_event *e; 948872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter 949872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter e = kmalloc(sizeof(*e), GFP_ATOMIC); 950cfb0c9d1ffbf930a4a852f178b161c522b21b0abStefan Richter if (e == NULL) 951872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter return; 952cfb0c9d1ffbf930a4a852f178b161c522b21b0abStefan Richter 953872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter e->interrupt.type = FW_CDEV_EVENT_ISO_INTERRUPT_MULTICHANNEL; 954872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter e->interrupt.closure = client->iso_closure; 955872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter e->interrupt.completed = fw_iso_buffer_lookup(&client->buffer, 956872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter completed); 957872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter queue_event(client, &e->event, &e->interrupt, 958872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter sizeof(e->interrupt), NULL, 0); 959872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter} 960872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter 9610b6c4857f7684f6d3f59e0506f62953575346978Stefan Richterstatic enum dma_data_direction iso_dma_direction(struct fw_iso_context *context) 9620b6c4857f7684f6d3f59e0506f62953575346978Stefan Richter{ 9630b6c4857f7684f6d3f59e0506f62953575346978Stefan Richter if (context->type == FW_ISO_CONTEXT_TRANSMIT) 9640b6c4857f7684f6d3f59e0506f62953575346978Stefan Richter return DMA_TO_DEVICE; 9650b6c4857f7684f6d3f59e0506f62953575346978Stefan Richter else 9660b6c4857f7684f6d3f59e0506f62953575346978Stefan Richter return DMA_FROM_DEVICE; 9670b6c4857f7684f6d3f59e0506f62953575346978Stefan Richter} 9680b6c4857f7684f6d3f59e0506f62953575346978Stefan Richter 9696e95dea728f4af36c033fcf2318529bd46dae540Stefan Richterstatic int ioctl_create_iso_context(struct client *client, union ioctl_arg *arg) 97019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{ 9716e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter struct fw_cdev_create_iso_context *a = &arg->create_iso_context; 97224315c5e6f508edd84e996d67daef3d1bcc72f8bKristian Høgsberg struct fw_iso_context *context; 973872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter fw_iso_callback_t cb; 9740b6c4857f7684f6d3f59e0506f62953575346978Stefan Richter int ret; 97519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 976eb5b35a560510efc6bb62f05c3c82e9596cdfafeStefan Richter BUILD_BUG_ON(FW_CDEV_ISO_CONTEXT_TRANSMIT != FW_ISO_CONTEXT_TRANSMIT || 977872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter FW_CDEV_ISO_CONTEXT_RECEIVE != FW_ISO_CONTEXT_RECEIVE || 978872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter FW_CDEV_ISO_CONTEXT_RECEIVE_MULTICHANNEL != 979872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter FW_ISO_CONTEXT_RECEIVE_MULTICHANNEL); 98021efb3cfc6ed49991638000f58bb23b838c76e25Kristian Høgsberg 9816e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter switch (a->type) { 982872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter case FW_ISO_CONTEXT_TRANSMIT: 983872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter if (a->speed > SCODE_3200 || a->channel > 63) 984c70dc788fd8d3870b41231b6a53a64afb98cfd13Kristian Høgsberg return -EINVAL; 985872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter 986872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter cb = iso_callback; 987c70dc788fd8d3870b41231b6a53a64afb98cfd13Kristian Høgsberg break; 988c70dc788fd8d3870b41231b6a53a64afb98cfd13Kristian Høgsberg 989872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter case FW_ISO_CONTEXT_RECEIVE: 990872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter if (a->header_size < 4 || (a->header_size & 3) || 991872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter a->channel > 63) 992c70dc788fd8d3870b41231b6a53a64afb98cfd13Kristian Høgsberg return -EINVAL; 993872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter 994872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter cb = iso_callback; 995872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter break; 996872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter 997872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter case FW_ISO_CONTEXT_RECEIVE_MULTICHANNEL: 998872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter cb = (fw_iso_callback_t)iso_mc_callback; 999c70dc788fd8d3870b41231b6a53a64afb98cfd13Kristian Høgsberg break; 1000c70dc788fd8d3870b41231b6a53a64afb98cfd13Kristian Høgsberg 1001c70dc788fd8d3870b41231b6a53a64afb98cfd13Kristian Høgsberg default: 100221efb3cfc6ed49991638000f58bb23b838c76e25Kristian Høgsberg return -EINVAL; 1003c70dc788fd8d3870b41231b6a53a64afb98cfd13Kristian Høgsberg } 1004c70dc788fd8d3870b41231b6a53a64afb98cfd13Kristian Høgsberg 10056e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter context = fw_iso_context_create(client->device->card, a->type, 1006872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter a->channel, a->speed, a->header_size, cb, client); 100724315c5e6f508edd84e996d67daef3d1bcc72f8bKristian Høgsberg if (IS_ERR(context)) 100824315c5e6f508edd84e996d67daef3d1bcc72f8bKristian Høgsberg return PTR_ERR(context); 10090699a73af3811b66b1ab5650575acee5eea841abClemens Ladisch if (client->version < FW_CDEV_VERSION_AUTO_FLUSH_ISO_OVERFLOW) 10100699a73af3811b66b1ab5650575acee5eea841abClemens Ladisch context->drop_overflow_headers = true; 101124315c5e6f508edd84e996d67daef3d1bcc72f8bKristian Høgsberg 1012bdfe273ee54b29498851fc8058516037d284270cClemens Ladisch /* We only support one context at this time. */ 1013bdfe273ee54b29498851fc8058516037d284270cClemens Ladisch spin_lock_irq(&client->lock); 1014bdfe273ee54b29498851fc8058516037d284270cClemens Ladisch if (client->iso_context != NULL) { 1015bdfe273ee54b29498851fc8058516037d284270cClemens Ladisch spin_unlock_irq(&client->lock); 1016bdfe273ee54b29498851fc8058516037d284270cClemens Ladisch fw_iso_context_destroy(context); 10170b6c4857f7684f6d3f59e0506f62953575346978Stefan Richter 1018bdfe273ee54b29498851fc8058516037d284270cClemens Ladisch return -EBUSY; 1019bdfe273ee54b29498851fc8058516037d284270cClemens Ladisch } 10200b6c4857f7684f6d3f59e0506f62953575346978Stefan Richter if (!client->buffer_is_mapped) { 10210b6c4857f7684f6d3f59e0506f62953575346978Stefan Richter ret = fw_iso_buffer_map_dma(&client->buffer, 10220b6c4857f7684f6d3f59e0506f62953575346978Stefan Richter client->device->card, 10230b6c4857f7684f6d3f59e0506f62953575346978Stefan Richter iso_dma_direction(context)); 10240b6c4857f7684f6d3f59e0506f62953575346978Stefan Richter if (ret < 0) { 10250b6c4857f7684f6d3f59e0506f62953575346978Stefan Richter spin_unlock_irq(&client->lock); 10260b6c4857f7684f6d3f59e0506f62953575346978Stefan Richter fw_iso_context_destroy(context); 10270b6c4857f7684f6d3f59e0506f62953575346978Stefan Richter 10280b6c4857f7684f6d3f59e0506f62953575346978Stefan Richter return ret; 10290b6c4857f7684f6d3f59e0506f62953575346978Stefan Richter } 10300b6c4857f7684f6d3f59e0506f62953575346978Stefan Richter client->buffer_is_mapped = true; 10310b6c4857f7684f6d3f59e0506f62953575346978Stefan Richter } 10326e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter client->iso_closure = a->closure; 103324315c5e6f508edd84e996d67daef3d1bcc72f8bKristian Høgsberg client->iso_context = context; 1034bdfe273ee54b29498851fc8058516037d284270cClemens Ladisch spin_unlock_irq(&client->lock); 103519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 10366e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter a->handle = 0; 1037abaa5743e340c23922d92c9a5a6753ea3ae71e58Kristian Høgsberg 103819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg return 0; 103919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg} 104019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 1041872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richterstatic int ioctl_set_iso_channels(struct client *client, union ioctl_arg *arg) 1042872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter{ 1043872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter struct fw_cdev_set_iso_channels *a = &arg->set_iso_channels; 1044872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter struct fw_iso_context *ctx = client->iso_context; 1045872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter 1046872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter if (ctx == NULL || a->handle != 0) 1047872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter return -EINVAL; 1048872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter 1049872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter return fw_iso_context_set_channels(ctx, &a->channels); 1050872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter} 1051872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter 10521ca31ae7cfed3e2a8e48fbf6ed6cac06495b6158Kristian Høgsberg/* Macros for decoding the iso packet control header. */ 10531ca31ae7cfed3e2a8e48fbf6ed6cac06495b6158Kristian Høgsberg#define GET_PAYLOAD_LENGTH(v) ((v) & 0xffff) 10541ca31ae7cfed3e2a8e48fbf6ed6cac06495b6158Kristian Høgsberg#define GET_INTERRUPT(v) (((v) >> 16) & 0x01) 10551ca31ae7cfed3e2a8e48fbf6ed6cac06495b6158Kristian Høgsberg#define GET_SKIP(v) (((v) >> 17) & 0x01) 10567a1003449c693f0d57443c8786bbf19717921ae0Stefan Richter#define GET_TAG(v) (((v) >> 18) & 0x03) 10577a1003449c693f0d57443c8786bbf19717921ae0Stefan Richter#define GET_SY(v) (((v) >> 20) & 0x0f) 10581ca31ae7cfed3e2a8e48fbf6ed6cac06495b6158Kristian Høgsberg#define GET_HEADER_LENGTH(v) (((v) >> 24) & 0xff) 10591ca31ae7cfed3e2a8e48fbf6ed6cac06495b6158Kristian Høgsberg 10606e95dea728f4af36c033fcf2318529bd46dae540Stefan Richterstatic int ioctl_queue_iso(struct client *client, union ioctl_arg *arg) 106119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{ 10626e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter struct fw_cdev_queue_iso *a = &arg->queue_iso; 106319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg struct fw_cdev_iso_packet __user *p, *end, *next; 10649b32d5f3074e9b1afaa39a360a59fd77a2214783Kristian Høgsberg struct fw_iso_context *ctx = client->iso_context; 1065872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter unsigned long payload, buffer_end, transmit_header_bytes = 0; 10661ca31ae7cfed3e2a8e48fbf6ed6cac06495b6158Kristian Høgsberg u32 control; 106719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg int count; 106819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg struct { 106919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg struct fw_iso_packet packet; 107019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg u8 header[256]; 107119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg } u; 107219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 10736e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter if (ctx == NULL || a->handle != 0) 107419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg return -EINVAL; 107519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 1076c781c06d119d04601727f2fbc30151e6760d536dKristian Høgsberg /* 1077c781c06d119d04601727f2fbc30151e6760d536dKristian Høgsberg * If the user passes a non-NULL data pointer, has mmap()'ed 107819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg * the iso buffer, and the pointer points inside the buffer, 107919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg * we setup the payload pointers accordingly. Otherwise we 10809aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg * set them both to 0, which will still let packets with 108119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg * payload_length == 0 through. In other words, if no packets 108219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg * use the indirect payload, the iso buffer need not be mapped 10836e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter * and the a->data pointer is ignored. 1084c781c06d119d04601727f2fbc30151e6760d536dKristian Høgsberg */ 10856e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter payload = (unsigned long)a->data - client->vm_start; 1086ef370ee74b7a9cb769d50bfb73b4023ee3e37719Kristian Høgsberg buffer_end = client->buffer.page_count << PAGE_SHIFT; 10876e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter if (a->data == 0 || client->buffer.pages == NULL || 1088ef370ee74b7a9cb769d50bfb73b4023ee3e37719Kristian Høgsberg payload >= buffer_end) { 10899aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg payload = 0; 1090ef370ee74b7a9cb769d50bfb73b4023ee3e37719Kristian Høgsberg buffer_end = 0; 109119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg } 109219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 1093872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter if (ctx->type == FW_ISO_CONTEXT_RECEIVE_MULTICHANNEL && payload & 3) 1094872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter return -EINVAL; 10951ccc9147f6a063c42fef67acff34de18435a4a6bAl Viro 1096872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter p = (struct fw_cdev_iso_packet __user *)u64_to_uptr(a->packets); 10976e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter if (!access_ok(VERIFY_READ, p, a->size)) 109819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg return -EFAULT; 109919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 11006e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter end = (void __user *)p + a->size; 110119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg count = 0; 110219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg while (p < end) { 11031ca31ae7cfed3e2a8e48fbf6ed6cac06495b6158Kristian Høgsberg if (get_user(control, &p->control)) 110419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg return -EFAULT; 11051ca31ae7cfed3e2a8e48fbf6ed6cac06495b6158Kristian Høgsberg u.packet.payload_length = GET_PAYLOAD_LENGTH(control); 11061ca31ae7cfed3e2a8e48fbf6ed6cac06495b6158Kristian Høgsberg u.packet.interrupt = GET_INTERRUPT(control); 11071ca31ae7cfed3e2a8e48fbf6ed6cac06495b6158Kristian Høgsberg u.packet.skip = GET_SKIP(control); 11081ca31ae7cfed3e2a8e48fbf6ed6cac06495b6158Kristian Høgsberg u.packet.tag = GET_TAG(control); 11091ca31ae7cfed3e2a8e48fbf6ed6cac06495b6158Kristian Høgsberg u.packet.sy = GET_SY(control); 11101ca31ae7cfed3e2a8e48fbf6ed6cac06495b6158Kristian Høgsberg u.packet.header_length = GET_HEADER_LENGTH(control); 1111295e3feb92e5073ec32a3c626302d4b92c4c8a95Kristian Høgsberg 1112872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter switch (ctx->type) { 1113872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter case FW_ISO_CONTEXT_TRANSMIT: 1114872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter if (u.packet.header_length & 3) 1115385ab5bcd4be586dffdba550b310308d89eade71Clemens Ladisch return -EINVAL; 1116ae2a97661482c1d0f1aa41b837da95054d0e9a1bStefan Richter transmit_header_bytes = u.packet.header_length; 1117872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter break; 1118872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter 1119872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter case FW_ISO_CONTEXT_RECEIVE: 112069e61d0c07fa28a05f699723a88d49e0014019b6Stefan Richter if (u.packet.header_length == 0 || 112169e61d0c07fa28a05f699723a88d49e0014019b6Stefan Richter u.packet.header_length % ctx->header_size != 0) 1122385ab5bcd4be586dffdba550b310308d89eade71Clemens Ladisch return -EINVAL; 1123872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter break; 1124872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter 1125872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter case FW_ISO_CONTEXT_RECEIVE_MULTICHANNEL: 1126872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter if (u.packet.payload_length == 0 || 1127872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter u.packet.payload_length & 3) 1128295e3feb92e5073ec32a3c626302d4b92c4c8a95Kristian Høgsberg return -EINVAL; 1129872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter break; 1130295e3feb92e5073ec32a3c626302d4b92c4c8a95Kristian Høgsberg } 1131295e3feb92e5073ec32a3c626302d4b92c4c8a95Kristian Høgsberg 113219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg next = (struct fw_cdev_iso_packet __user *) 1133ae2a97661482c1d0f1aa41b837da95054d0e9a1bStefan Richter &p->header[transmit_header_bytes / 4]; 113419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg if (next > end) 113519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg return -EINVAL; 113619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg if (__copy_from_user 1137ae2a97661482c1d0f1aa41b837da95054d0e9a1bStefan Richter (u.packet.header, p->header, transmit_header_bytes)) 113819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg return -EFAULT; 113998b6cbe83b6e8db54638746c9040c7962d96b322Kristian Høgsberg if (u.packet.skip && ctx->type == FW_ISO_CONTEXT_TRANSMIT && 114019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg u.packet.header_length + u.packet.payload_length > 0) 114119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg return -EINVAL; 1142ef370ee74b7a9cb769d50bfb73b4023ee3e37719Kristian Høgsberg if (payload + u.packet.payload_length > buffer_end) 114319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg return -EINVAL; 114419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 11459b32d5f3074e9b1afaa39a360a59fd77a2214783Kristian Høgsberg if (fw_iso_context_queue(ctx, &u.packet, 11469b32d5f3074e9b1afaa39a360a59fd77a2214783Kristian Høgsberg &client->buffer, payload)) 114719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg break; 114819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 114919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg p = next; 115019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg payload += u.packet.payload_length; 115119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg count++; 115219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg } 115313882a82ee1646336c3996c93b4a560a55d2a419Clemens Ladisch fw_iso_context_queue_flush(ctx); 115419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 11556e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter a->size -= uptr_to_u64(p) - a->packets; 11566e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter a->packets = uptr_to_u64(p); 11576e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter a->data = client->vm_start + payload; 115819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 115919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg return count; 116019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg} 116119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 11626e95dea728f4af36c033fcf2318529bd46dae540Stefan Richterstatic int ioctl_start_iso(struct client *client, union ioctl_arg *arg) 116319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{ 11646e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter struct fw_cdev_start_iso *a = &arg->start_iso; 116519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 1166eb5b35a560510efc6bb62f05c3c82e9596cdfafeStefan Richter BUILD_BUG_ON( 1167eb5b35a560510efc6bb62f05c3c82e9596cdfafeStefan Richter FW_CDEV_ISO_CONTEXT_MATCH_TAG0 != FW_ISO_CONTEXT_MATCH_TAG0 || 1168eb5b35a560510efc6bb62f05c3c82e9596cdfafeStefan Richter FW_CDEV_ISO_CONTEXT_MATCH_TAG1 != FW_ISO_CONTEXT_MATCH_TAG1 || 1169eb5b35a560510efc6bb62f05c3c82e9596cdfafeStefan Richter FW_CDEV_ISO_CONTEXT_MATCH_TAG2 != FW_ISO_CONTEXT_MATCH_TAG2 || 1170eb5b35a560510efc6bb62f05c3c82e9596cdfafeStefan Richter FW_CDEV_ISO_CONTEXT_MATCH_TAG3 != FW_ISO_CONTEXT_MATCH_TAG3 || 1171eb5b35a560510efc6bb62f05c3c82e9596cdfafeStefan Richter FW_CDEV_ISO_CONTEXT_MATCH_ALL_TAGS != FW_ISO_CONTEXT_MATCH_ALL_TAGS); 1172eb5b35a560510efc6bb62f05c3c82e9596cdfafeStefan Richter 11736e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter if (client->iso_context == NULL || a->handle != 0) 1174abaa5743e340c23922d92c9a5a6753ea3ae71e58Kristian Høgsberg return -EINVAL; 1175fae603121428ba83b7343c88e68a7144525ab3ebStefan Richter 11766e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter if (client->iso_context->type == FW_ISO_CONTEXT_RECEIVE && 11776e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter (a->tags == 0 || a->tags > 15 || a->sync > 15)) 11786e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter return -EINVAL; 1179eb0306eac0aad0b7da18d8fbfb777f155b2c010dKristian Høgsberg 11806e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter return fw_iso_context_start(client->iso_context, 11816e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter a->cycle, a->sync, a->tags); 118219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg} 118319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 11846e95dea728f4af36c033fcf2318529bd46dae540Stefan Richterstatic int ioctl_stop_iso(struct client *client, union ioctl_arg *arg) 1185b82956685aab4a9d333714300eb8a86fed6c9ab3Kristian Høgsberg{ 11866e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter struct fw_cdev_stop_iso *a = &arg->stop_iso; 1187abaa5743e340c23922d92c9a5a6753ea3ae71e58Kristian Høgsberg 11886e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter if (client->iso_context == NULL || a->handle != 0) 1189abaa5743e340c23922d92c9a5a6753ea3ae71e58Kristian Høgsberg return -EINVAL; 1190abaa5743e340c23922d92c9a5a6753ea3ae71e58Kristian Høgsberg 1191b82956685aab4a9d333714300eb8a86fed6c9ab3Kristian Høgsberg return fw_iso_context_stop(client->iso_context); 1192b82956685aab4a9d333714300eb8a86fed6c9ab3Kristian Høgsberg} 1193b82956685aab4a9d333714300eb8a86fed6c9ab3Kristian Høgsberg 1194d1bbd20972936b9b178fda3eb1ec417cb27fdc01Clemens Ladischstatic int ioctl_flush_iso(struct client *client, union ioctl_arg *arg) 1195d1bbd20972936b9b178fda3eb1ec417cb27fdc01Clemens Ladisch{ 1196d1bbd20972936b9b178fda3eb1ec417cb27fdc01Clemens Ladisch struct fw_cdev_flush_iso *a = &arg->flush_iso; 1197d1bbd20972936b9b178fda3eb1ec417cb27fdc01Clemens Ladisch 1198d1bbd20972936b9b178fda3eb1ec417cb27fdc01Clemens Ladisch if (client->iso_context == NULL || a->handle != 0) 1199d1bbd20972936b9b178fda3eb1ec417cb27fdc01Clemens Ladisch return -EINVAL; 1200d1bbd20972936b9b178fda3eb1ec417cb27fdc01Clemens Ladisch 1201d1bbd20972936b9b178fda3eb1ec417cb27fdc01Clemens Ladisch return fw_iso_context_flush_completions(client->iso_context); 1202d1bbd20972936b9b178fda3eb1ec417cb27fdc01Clemens Ladisch} 1203d1bbd20972936b9b178fda3eb1ec417cb27fdc01Clemens Ladisch 12046e95dea728f4af36c033fcf2318529bd46dae540Stefan Richterstatic int ioctl_get_cycle_timer2(struct client *client, union ioctl_arg *arg) 1205a64408b96b5f67c2778958a230b5cfa3408a4a81Stefan Richter{ 12066e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter struct fw_cdev_get_cycle_timer2 *a = &arg->get_cycle_timer2; 1207a64408b96b5f67c2778958a230b5cfa3408a4a81Stefan Richter struct fw_card *card = client->device->card; 1208abfe5a01ef1e463cbafdae461b693db34e308c02Stefan Richter struct timespec ts = {0, 0}; 12094a9bde9b8ab55a2bb51b57cad215a97bcf80bae2Stefan Richter u32 cycle_time; 1210abfe5a01ef1e463cbafdae461b693db34e308c02Stefan Richter int ret = 0; 1211a64408b96b5f67c2778958a230b5cfa3408a4a81Stefan Richter 12124a9bde9b8ab55a2bb51b57cad215a97bcf80bae2Stefan Richter local_irq_disable(); 1213a64408b96b5f67c2778958a230b5cfa3408a4a81Stefan Richter 12140fcff4e39323f466a47684d7c8ffa77e1be86c8aStefan Richter cycle_time = card->driver->read_csr(card, CSR_CYCLE_TIME); 1215abfe5a01ef1e463cbafdae461b693db34e308c02Stefan Richter 12166e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter switch (a->clk_id) { 1217889235ce2b26cdd231c7a1d6005928ec42f11637Thomas Gleixner case CLOCK_REALTIME: getnstimeofday(&ts); break; 1218889235ce2b26cdd231c7a1d6005928ec42f11637Thomas Gleixner case CLOCK_MONOTONIC: ktime_get_ts(&ts); break; 1219889235ce2b26cdd231c7a1d6005928ec42f11637Thomas Gleixner case CLOCK_MONOTONIC_RAW: getrawmonotonic(&ts); break; 1220abfe5a01ef1e463cbafdae461b693db34e308c02Stefan Richter default: 1221abfe5a01ef1e463cbafdae461b693db34e308c02Stefan Richter ret = -EINVAL; 1222abfe5a01ef1e463cbafdae461b693db34e308c02Stefan Richter } 1223a64408b96b5f67c2778958a230b5cfa3408a4a81Stefan Richter 12244a9bde9b8ab55a2bb51b57cad215a97bcf80bae2Stefan Richter local_irq_enable(); 1225a64408b96b5f67c2778958a230b5cfa3408a4a81Stefan Richter 12266e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter a->tv_sec = ts.tv_sec; 12276e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter a->tv_nsec = ts.tv_nsec; 12286e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter a->cycle_timer = cycle_time; 1229abfe5a01ef1e463cbafdae461b693db34e308c02Stefan Richter 1230abfe5a01ef1e463cbafdae461b693db34e308c02Stefan Richter return ret; 1231abfe5a01ef1e463cbafdae461b693db34e308c02Stefan Richter} 1232abfe5a01ef1e463cbafdae461b693db34e308c02Stefan Richter 12336e95dea728f4af36c033fcf2318529bd46dae540Stefan Richterstatic int ioctl_get_cycle_timer(struct client *client, union ioctl_arg *arg) 1234abfe5a01ef1e463cbafdae461b693db34e308c02Stefan Richter{ 12356e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter struct fw_cdev_get_cycle_timer *a = &arg->get_cycle_timer; 1236abfe5a01ef1e463cbafdae461b693db34e308c02Stefan Richter struct fw_cdev_get_cycle_timer2 ct2; 1237abfe5a01ef1e463cbafdae461b693db34e308c02Stefan Richter 1238abfe5a01ef1e463cbafdae461b693db34e308c02Stefan Richter ct2.clk_id = CLOCK_REALTIME; 12396e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter ioctl_get_cycle_timer2(client, (union ioctl_arg *)&ct2); 1240abfe5a01ef1e463cbafdae461b693db34e308c02Stefan Richter 12416e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter a->local_time = ct2.tv_sec * USEC_PER_SEC + ct2.tv_nsec / NSEC_PER_USEC; 12426e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter a->cycle_timer = ct2.cycle_timer; 12434a9bde9b8ab55a2bb51b57cad215a97bcf80bae2Stefan Richter 1244a64408b96b5f67c2778958a230b5cfa3408a4a81Stefan Richter return 0; 1245a64408b96b5f67c2778958a230b5cfa3408a4a81Stefan Richter} 1246a64408b96b5f67c2778958a230b5cfa3408a4a81Stefan Richter 1247b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richterstatic void iso_resource_work(struct work_struct *work) 1248b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter{ 1249b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter struct iso_resource_event *e; 1250b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter struct iso_resource *r = 1251b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter container_of(work, struct iso_resource, work.work); 1252b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter struct client *client = r->client; 1253b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter int generation, channel, bandwidth, todo; 1254b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter bool skip, free, success; 1255b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter 1256b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter spin_lock_irq(&client->lock); 1257b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter generation = client->device->generation; 1258b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter todo = r->todo; 1259b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter /* Allow 1000ms grace period for other reallocations. */ 1260b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter if (todo == ISO_RES_ALLOC && 1261e71084af58cf15e6043338500eeaf6281d0a62afClemens Ladisch time_before64(get_jiffies_64(), 1262e71084af58cf15e6043338500eeaf6281d0a62afClemens Ladisch client->device->card->reset_jiffies + HZ)) { 12639fb551bf72929b316abb6d96cfb2ec05e896042aStefan Richter schedule_iso_resource(r, DIV_ROUND_UP(HZ, 3)); 1264b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter skip = true; 1265b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter } else { 1266b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter /* We could be called twice within the same generation. */ 1267b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter skip = todo == ISO_RES_REALLOC && 1268b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter r->generation == generation; 1269b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter } 12701ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter free = todo == ISO_RES_DEALLOC || 12711ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter todo == ISO_RES_ALLOC_ONCE || 12721ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter todo == ISO_RES_DEALLOC_ONCE; 1273b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter r->generation = generation; 1274b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter spin_unlock_irq(&client->lock); 1275b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter 1276b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter if (skip) 1277b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter goto out; 1278b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter 1279b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter bandwidth = r->bandwidth; 1280b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter 1281b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter fw_iso_resource_manage(client->device->card, generation, 1282b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter r->channels, &channel, &bandwidth, 12831ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter todo == ISO_RES_ALLOC || 12841ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter todo == ISO_RES_REALLOC || 1285f30e6d3e419bfb5540fa82ba7eca01d578556e6bStefan Richter todo == ISO_RES_ALLOC_ONCE); 1286b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter /* 1287b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter * Is this generation outdated already? As long as this resource sticks 1288b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter * in the idr, it will be scheduled again for a newer generation or at 1289b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter * shutdown. 1290b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter */ 1291b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter if (channel == -EAGAIN && 1292b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter (todo == ISO_RES_ALLOC || todo == ISO_RES_REALLOC)) 1293b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter goto out; 1294b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter 1295b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter success = channel >= 0 || bandwidth > 0; 1296b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter 1297b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter spin_lock_irq(&client->lock); 1298b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter /* 1299b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter * Transit from allocation to reallocation, except if the client 1300b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter * requested deallocation in the meantime. 1301b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter */ 1302b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter if (r->todo == ISO_RES_ALLOC) 1303b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter r->todo = ISO_RES_REALLOC; 1304b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter /* 1305b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter * Allocation or reallocation failure? Pull this resource out of the 1306b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter * idr and prepare for deletion, unless the client is shutting down. 1307b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter */ 1308b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter if (r->todo == ISO_RES_REALLOC && !success && 1309b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter !client->in_shutdown && 1310b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter idr_find(&client->resource_idr, r->resource.handle)) { 1311b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter idr_remove(&client->resource_idr, r->resource.handle); 1312b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter client_put(client); 1313b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter free = true; 1314b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter } 1315b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter spin_unlock_irq(&client->lock); 1316b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter 1317b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter if (todo == ISO_RES_ALLOC && channel >= 0) 13185d9cb7d276a9c465fef5a771792eac2cf1929f2bStefan Richter r->channels = 1ULL << channel; 1319b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter 1320b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter if (todo == ISO_RES_REALLOC && success) 1321b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter goto out; 1322b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter 13231ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter if (todo == ISO_RES_ALLOC || todo == ISO_RES_ALLOC_ONCE) { 1324b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter e = r->e_alloc; 1325b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter r->e_alloc = NULL; 1326b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter } else { 1327b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter e = r->e_dealloc; 1328b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter r->e_dealloc = NULL; 1329b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter } 1330e21fcf798e246202d7b60e864f1d7302ebaaf41cStefan Richter e->iso_resource.handle = r->resource.handle; 1331e21fcf798e246202d7b60e864f1d7302ebaaf41cStefan Richter e->iso_resource.channel = channel; 1332e21fcf798e246202d7b60e864f1d7302ebaaf41cStefan Richter e->iso_resource.bandwidth = bandwidth; 1333b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter 1334b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter queue_event(client, &e->event, 1335e21fcf798e246202d7b60e864f1d7302ebaaf41cStefan Richter &e->iso_resource, sizeof(e->iso_resource), NULL, 0); 1336b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter 1337b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter if (free) { 1338b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter cancel_delayed_work(&r->work); 1339b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter kfree(r->e_alloc); 1340b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter kfree(r->e_dealloc); 1341b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter kfree(r); 1342b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter } 1343b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter out: 1344b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter client_put(client); 1345b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter} 1346b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter 1347b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richterstatic void release_iso_resource(struct client *client, 1348b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter struct client_resource *resource) 1349b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter{ 1350b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter struct iso_resource *r = 1351b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter container_of(resource, struct iso_resource, resource); 1352b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter 1353b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter spin_lock_irq(&client->lock); 1354b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter r->todo = ISO_RES_DEALLOC; 13559fb551bf72929b316abb6d96cfb2ec05e896042aStefan Richter schedule_iso_resource(r, 0); 1356b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter spin_unlock_irq(&client->lock); 1357b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter} 1358b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter 13591ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richterstatic int init_iso_resource(struct client *client, 13601ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter struct fw_cdev_allocate_iso_resource *request, int todo) 1361b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter{ 1362b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter struct iso_resource_event *e1, *e2; 1363b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter struct iso_resource *r; 1364b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter int ret; 1365b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter 1366b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter if ((request->channels == 0 && request->bandwidth == 0) || 1367bdabfa54635e5d95a5aa2087794f203cc1915f7aStefan Richter request->bandwidth > BANDWIDTH_AVAILABLE_INITIAL) 1368b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter return -EINVAL; 1369b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter 1370b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter r = kmalloc(sizeof(*r), GFP_KERNEL); 1371b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter e1 = kmalloc(sizeof(*e1), GFP_KERNEL); 1372b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter e2 = kmalloc(sizeof(*e2), GFP_KERNEL); 1373b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter if (r == NULL || e1 == NULL || e2 == NULL) { 1374b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter ret = -ENOMEM; 1375b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter goto fail; 1376b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter } 1377b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter 1378b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter INIT_DELAYED_WORK(&r->work, iso_resource_work); 1379b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter r->client = client; 13801ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter r->todo = todo; 1381b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter r->generation = -1; 1382b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter r->channels = request->channels; 1383b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter r->bandwidth = request->bandwidth; 1384b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter r->e_alloc = e1; 1385b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter r->e_dealloc = e2; 1386b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter 1387e21fcf798e246202d7b60e864f1d7302ebaaf41cStefan Richter e1->iso_resource.closure = request->closure; 1388e21fcf798e246202d7b60e864f1d7302ebaaf41cStefan Richter e1->iso_resource.type = FW_CDEV_EVENT_ISO_RESOURCE_ALLOCATED; 1389e21fcf798e246202d7b60e864f1d7302ebaaf41cStefan Richter e2->iso_resource.closure = request->closure; 1390e21fcf798e246202d7b60e864f1d7302ebaaf41cStefan Richter e2->iso_resource.type = FW_CDEV_EVENT_ISO_RESOURCE_DEALLOCATED; 1391b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter 13921ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter if (todo == ISO_RES_ALLOC) { 13931ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter r->resource.release = release_iso_resource; 13941ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter ret = add_client_resource(client, &r->resource, GFP_KERNEL); 139581610b8fbfc027a67707ff567d490819a3d55844Stefan Richter if (ret < 0) 139681610b8fbfc027a67707ff567d490819a3d55844Stefan Richter goto fail; 13971ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter } else { 13981ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter r->resource.release = NULL; 13991ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter r->resource.handle = -1; 14009fb551bf72929b316abb6d96cfb2ec05e896042aStefan Richter schedule_iso_resource(r, 0); 14011ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter } 1402b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter request->handle = r->resource.handle; 1403b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter 1404b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter return 0; 1405b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter fail: 1406b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter kfree(r); 1407b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter kfree(e1); 1408b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter kfree(e2); 1409b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter 1410b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter return ret; 1411b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter} 1412b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter 14136e95dea728f4af36c033fcf2318529bd46dae540Stefan Richterstatic int ioctl_allocate_iso_resource(struct client *client, 14146e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter union ioctl_arg *arg) 14151ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter{ 14166e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter return init_iso_resource(client, 14176e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter &arg->allocate_iso_resource, ISO_RES_ALLOC); 14181ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter} 14191ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter 14206e95dea728f4af36c033fcf2318529bd46dae540Stefan Richterstatic int ioctl_deallocate_iso_resource(struct client *client, 14216e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter union ioctl_arg *arg) 1422b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter{ 14236e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter return release_client_resource(client, 14246e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter arg->deallocate.handle, release_iso_resource, NULL); 1425b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter} 1426b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter 14276e95dea728f4af36c033fcf2318529bd46dae540Stefan Richterstatic int ioctl_allocate_iso_resource_once(struct client *client, 14286e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter union ioctl_arg *arg) 14291ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter{ 14306e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter return init_iso_resource(client, 14316e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter &arg->allocate_iso_resource, ISO_RES_ALLOC_ONCE); 14321ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter} 14331ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter 14346e95dea728f4af36c033fcf2318529bd46dae540Stefan Richterstatic int ioctl_deallocate_iso_resource_once(struct client *client, 14356e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter union ioctl_arg *arg) 14361ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter{ 14376e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter return init_iso_resource(client, 14386e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter &arg->allocate_iso_resource, ISO_RES_DEALLOC_ONCE); 14391ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter} 14401ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter 1441c8a25900f35e575938c791507894c036c0f2ca7dStefan Richter/* 1442c8a25900f35e575938c791507894c036c0f2ca7dStefan Richter * Returns a speed code: Maximum speed to or from this device, 1443c8a25900f35e575938c791507894c036c0f2ca7dStefan Richter * limited by the device's link speed, the local node's link speed, 1444c8a25900f35e575938c791507894c036c0f2ca7dStefan Richter * and all PHY port speeds between the two links. 1445c8a25900f35e575938c791507894c036c0f2ca7dStefan Richter */ 14466e95dea728f4af36c033fcf2318529bd46dae540Stefan Richterstatic int ioctl_get_speed(struct client *client, union ioctl_arg *arg) 144733580a3ef5ba3bc0ee1b520df82a24bb37ce28f0Stefan Richter{ 1448c8a25900f35e575938c791507894c036c0f2ca7dStefan Richter return client->device->max_speed; 144933580a3ef5ba3bc0ee1b520df82a24bb37ce28f0Stefan Richter} 145033580a3ef5ba3bc0ee1b520df82a24bb37ce28f0Stefan Richter 14516e95dea728f4af36c033fcf2318529bd46dae540Stefan Richterstatic int ioctl_send_broadcast_request(struct client *client, 14526e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter union ioctl_arg *arg) 1453acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter{ 14546e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter struct fw_cdev_send_request *a = &arg->send_request; 1455acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter 14566e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter switch (a->tcode) { 1457acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter case TCODE_WRITE_QUADLET_REQUEST: 1458acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter case TCODE_WRITE_BLOCK_REQUEST: 1459acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter break; 1460acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter default: 1461acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter return -EINVAL; 1462acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter } 1463acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter 14641566f3dc3e5986a16c7bbb3bb95bb691251a8d25Stefan Richter /* Security policy: Only allow accesses to Units Space. */ 14656e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter if (a->offset < CSR_REGISTER_BASE + CSR_CONFIG_ROM_END) 14661566f3dc3e5986a16c7bbb3bb95bb691251a8d25Stefan Richter return -EACCES; 14671566f3dc3e5986a16c7bbb3bb95bb691251a8d25Stefan Richter 14686e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter return init_request(client, a, LOCAL_BUS | 0x3f, SCODE_100); 1469acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter} 1470acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter 14716e95dea728f4af36c033fcf2318529bd46dae540Stefan Richterstatic int ioctl_send_stream_packet(struct client *client, union ioctl_arg *arg) 1472f8c2287c65f8f72000102fc058232669e4540bc4Jay Fenlason{ 14736e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter struct fw_cdev_send_stream_packet *a = &arg->send_stream_packet; 147418e9b10fcdc090d3a38606958167d5923c7099b7Stefan Richter struct fw_cdev_send_request request; 147518e9b10fcdc090d3a38606958167d5923c7099b7Stefan Richter int dest; 1476f8c2287c65f8f72000102fc058232669e4540bc4Jay Fenlason 14776e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter if (a->speed > client->device->card->link_speed || 14786e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter a->length > 1024 << a->speed) 147918e9b10fcdc090d3a38606958167d5923c7099b7Stefan Richter return -EIO; 1480f8c2287c65f8f72000102fc058232669e4540bc4Jay Fenlason 14816e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter if (a->tag > 3 || a->channel > 63 || a->sy > 15) 148218e9b10fcdc090d3a38606958167d5923c7099b7Stefan Richter return -EINVAL; 148318e9b10fcdc090d3a38606958167d5923c7099b7Stefan Richter 14846e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter dest = fw_stream_packet_destination_id(a->tag, a->channel, a->sy); 148518e9b10fcdc090d3a38606958167d5923c7099b7Stefan Richter request.tcode = TCODE_STREAM_DATA; 14866e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter request.length = a->length; 14876e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter request.closure = a->closure; 14886e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter request.data = a->data; 14896e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter request.generation = a->generation; 149018e9b10fcdc090d3a38606958167d5923c7099b7Stefan Richter 14916e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter return init_request(client, &request, dest, a->speed); 1492f8c2287c65f8f72000102fc058232669e4540bc4Jay Fenlason} 1493f8c2287c65f8f72000102fc058232669e4540bc4Jay Fenlason 1494850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richterstatic void outbound_phy_packet_callback(struct fw_packet *packet, 1495850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter struct fw_card *card, int status) 1496850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter{ 1497850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter struct outbound_phy_packet_event *e = 1498850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter container_of(packet, struct outbound_phy_packet_event, p); 1499850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter 1500850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter switch (status) { 1501850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter /* expected: */ 1502850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter case ACK_COMPLETE: e->phy_packet.rcode = RCODE_COMPLETE; break; 1503850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter /* should never happen with PHY packets: */ 1504850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter case ACK_PENDING: e->phy_packet.rcode = RCODE_COMPLETE; break; 1505850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter case ACK_BUSY_X: 1506850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter case ACK_BUSY_A: 1507850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter case ACK_BUSY_B: e->phy_packet.rcode = RCODE_BUSY; break; 1508850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter case ACK_DATA_ERROR: e->phy_packet.rcode = RCODE_DATA_ERROR; break; 1509850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter case ACK_TYPE_ERROR: e->phy_packet.rcode = RCODE_TYPE_ERROR; break; 1510850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter /* stale generation; cancelled; on certain controllers: no ack */ 1511850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter default: e->phy_packet.rcode = status; break; 1512850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter } 1513cc550216ae9a2993ef3973464714dc1a39ab1f86Stefan Richter e->phy_packet.data[0] = packet->timestamp; 1514850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter 1515cc550216ae9a2993ef3973464714dc1a39ab1f86Stefan Richter queue_event(e->client, &e->event, &e->phy_packet, 1516cc550216ae9a2993ef3973464714dc1a39ab1f86Stefan Richter sizeof(e->phy_packet) + e->phy_packet.length, NULL, 0); 1517850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter client_put(e->client); 1518850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter} 1519850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter 1520850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richterstatic int ioctl_send_phy_packet(struct client *client, union ioctl_arg *arg) 1521850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter{ 1522850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter struct fw_cdev_send_phy_packet *a = &arg->send_phy_packet; 1523850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter struct fw_card *card = client->device->card; 1524850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter struct outbound_phy_packet_event *e; 1525850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter 1526850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter /* Access policy: Allow this ioctl only on local nodes' device files. */ 1527850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter if (!client->device->is_local) 1528850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter return -ENOSYS; 1529850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter 1530cc550216ae9a2993ef3973464714dc1a39ab1f86Stefan Richter e = kzalloc(sizeof(*e) + 4, GFP_KERNEL); 1531850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter if (e == NULL) 1532850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter return -ENOMEM; 1533850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter 1534850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter client_get(client); 1535850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter e->client = client; 1536850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter e->p.speed = SCODE_100; 1537850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter e->p.generation = a->generation; 15385b06db166c4d38638980283505259fa165d4f369Clemens Ladisch e->p.header[0] = TCODE_LINK_INTERNAL << 4; 15395b06db166c4d38638980283505259fa165d4f369Clemens Ladisch e->p.header[1] = a->data[0]; 15405b06db166c4d38638980283505259fa165d4f369Clemens Ladisch e->p.header[2] = a->data[1]; 15415b06db166c4d38638980283505259fa165d4f369Clemens Ladisch e->p.header_length = 12; 1542850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter e->p.callback = outbound_phy_packet_callback; 1543850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter e->phy_packet.closure = a->closure; 1544850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter e->phy_packet.type = FW_CDEV_EVENT_PHY_PACKET_SENT; 1545cc550216ae9a2993ef3973464714dc1a39ab1f86Stefan Richter if (is_ping_packet(a->data)) 1546cc550216ae9a2993ef3973464714dc1a39ab1f86Stefan Richter e->phy_packet.length = 4; 1547850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter 1548850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter card->driver->send_request(card, &e->p); 1549850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter 1550850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter return 0; 1551850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter} 1552850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter 1553bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richterstatic int ioctl_receive_phy_packets(struct client *client, union ioctl_arg *arg) 1554bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter{ 1555bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter struct fw_cdev_receive_phy_packets *a = &arg->receive_phy_packets; 1556bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter struct fw_card *card = client->device->card; 1557bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter 1558bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter /* Access policy: Allow this ioctl only on local nodes' device files. */ 1559bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter if (!client->device->is_local) 1560bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter return -ENOSYS; 1561bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter 1562bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter spin_lock_irq(&card->lock); 1563bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter 1564bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter list_move_tail(&client->phy_receiver_link, &card->phy_receiver_list); 1565bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter client->phy_receiver_closure = a->closure; 1566bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter 1567bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter spin_unlock_irq(&card->lock); 1568bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter 1569bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter return 0; 1570bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter} 1571bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter 1572bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richtervoid fw_cdev_handle_phy_packet(struct fw_card *card, struct fw_packet *p) 1573bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter{ 1574bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter struct client *client; 1575bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter struct inbound_phy_packet_event *e; 1576bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter unsigned long flags; 1577bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter 1578bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter spin_lock_irqsave(&card->lock, flags); 1579bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter 1580bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter list_for_each_entry(client, &card->phy_receiver_list, phy_receiver_link) { 1581bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter e = kmalloc(sizeof(*e) + 8, GFP_ATOMIC); 1582cfb0c9d1ffbf930a4a852f178b161c522b21b0abStefan Richter if (e == NULL) 1583bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter break; 1584cfb0c9d1ffbf930a4a852f178b161c522b21b0abStefan Richter 1585bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter e->phy_packet.closure = client->phy_receiver_closure; 1586bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter e->phy_packet.type = FW_CDEV_EVENT_PHY_PACKET_RECEIVED; 1587bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter e->phy_packet.rcode = RCODE_COMPLETE; 1588bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter e->phy_packet.length = 8; 1589bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter e->phy_packet.data[0] = p->header[1]; 1590bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter e->phy_packet.data[1] = p->header[2]; 1591bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter queue_event(client, &e->event, 1592bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter &e->phy_packet, sizeof(e->phy_packet) + 8, NULL, 0); 1593bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter } 1594bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter 1595bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter spin_unlock_irqrestore(&card->lock, flags); 1596bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter} 1597bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter 15986e95dea728f4af36c033fcf2318529bd46dae540Stefan Richterstatic int (* const ioctl_handlers[])(struct client *, union ioctl_arg *) = { 1599b9dc61cf404165fb77e80c853e9fec9af258f9ceStefan Richter [0x00] = ioctl_get_info, 1600b9dc61cf404165fb77e80c853e9fec9af258f9ceStefan Richter [0x01] = ioctl_send_request, 1601b9dc61cf404165fb77e80c853e9fec9af258f9ceStefan Richter [0x02] = ioctl_allocate, 1602b9dc61cf404165fb77e80c853e9fec9af258f9ceStefan Richter [0x03] = ioctl_deallocate, 1603b9dc61cf404165fb77e80c853e9fec9af258f9ceStefan Richter [0x04] = ioctl_send_response, 1604b9dc61cf404165fb77e80c853e9fec9af258f9ceStefan Richter [0x05] = ioctl_initiate_bus_reset, 1605b9dc61cf404165fb77e80c853e9fec9af258f9ceStefan Richter [0x06] = ioctl_add_descriptor, 1606b9dc61cf404165fb77e80c853e9fec9af258f9ceStefan Richter [0x07] = ioctl_remove_descriptor, 1607b9dc61cf404165fb77e80c853e9fec9af258f9ceStefan Richter [0x08] = ioctl_create_iso_context, 1608b9dc61cf404165fb77e80c853e9fec9af258f9ceStefan Richter [0x09] = ioctl_queue_iso, 1609b9dc61cf404165fb77e80c853e9fec9af258f9ceStefan Richter [0x0a] = ioctl_start_iso, 1610b9dc61cf404165fb77e80c853e9fec9af258f9ceStefan Richter [0x0b] = ioctl_stop_iso, 1611b9dc61cf404165fb77e80c853e9fec9af258f9ceStefan Richter [0x0c] = ioctl_get_cycle_timer, 1612b9dc61cf404165fb77e80c853e9fec9af258f9ceStefan Richter [0x0d] = ioctl_allocate_iso_resource, 1613b9dc61cf404165fb77e80c853e9fec9af258f9ceStefan Richter [0x0e] = ioctl_deallocate_iso_resource, 1614b9dc61cf404165fb77e80c853e9fec9af258f9ceStefan Richter [0x0f] = ioctl_allocate_iso_resource_once, 1615b9dc61cf404165fb77e80c853e9fec9af258f9ceStefan Richter [0x10] = ioctl_deallocate_iso_resource_once, 1616b9dc61cf404165fb77e80c853e9fec9af258f9ceStefan Richter [0x11] = ioctl_get_speed, 1617b9dc61cf404165fb77e80c853e9fec9af258f9ceStefan Richter [0x12] = ioctl_send_broadcast_request, 1618b9dc61cf404165fb77e80c853e9fec9af258f9ceStefan Richter [0x13] = ioctl_send_stream_packet, 1619b9dc61cf404165fb77e80c853e9fec9af258f9ceStefan Richter [0x14] = ioctl_get_cycle_timer2, 1620850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter [0x15] = ioctl_send_phy_packet, 1621bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter [0x16] = ioctl_receive_phy_packets, 1622872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter [0x17] = ioctl_set_iso_channels, 1623d1bbd20972936b9b178fda3eb1ec417cb27fdc01Clemens Ladisch [0x18] = ioctl_flush_iso, 16244f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg}; 16254f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg 162653dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richterstatic int dispatch_ioctl(struct client *client, 162753dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richter unsigned int cmd, void __user *arg) 162819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{ 16296e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter union ioctl_arg buffer; 16302dbd7d7e2327b0c2cc4e2de903e1cfa19980a504Stefan Richter int ret; 16314f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg 163264582298b9c29535188380f488873e7d2196a2ebStefan Richter if (fw_device_is_shutdown(client->device)) 163364582298b9c29535188380f488873e7d2196a2ebStefan Richter return -ENODEV; 163464582298b9c29535188380f488873e7d2196a2ebStefan Richter 16354f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg if (_IOC_TYPE(cmd) != '#' || 16369cac00b8f0079d5d3d54ec4dae453d58dec30e7cStefan Richter _IOC_NR(cmd) >= ARRAY_SIZE(ioctl_handlers) || 16379cac00b8f0079d5d3d54ec4dae453d58dec30e7cStefan Richter _IOC_SIZE(cmd) > sizeof(buffer)) 1638d873d794235efa590ab3c94d5ee22bb1fab19ac4Stefan Richter return -ENOTTY; 16394f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg 1640eaca2d8e75e90a70a63a6695c9f61932609db212Stefan Richter memset(&buffer, 0, sizeof(buffer)); 16419cac00b8f0079d5d3d54ec4dae453d58dec30e7cStefan Richter 16429cac00b8f0079d5d3d54ec4dae453d58dec30e7cStefan Richter if (_IOC_DIR(cmd) & _IOC_WRITE) 16439cac00b8f0079d5d3d54ec4dae453d58dec30e7cStefan Richter if (copy_from_user(&buffer, arg, _IOC_SIZE(cmd))) 16444f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg return -EFAULT; 16454f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg 16466e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter ret = ioctl_handlers[_IOC_NR(cmd)](client, &buffer); 16472dbd7d7e2327b0c2cc4e2de903e1cfa19980a504Stefan Richter if (ret < 0) 16482dbd7d7e2327b0c2cc4e2de903e1cfa19980a504Stefan Richter return ret; 16494f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg 16509cac00b8f0079d5d3d54ec4dae453d58dec30e7cStefan Richter if (_IOC_DIR(cmd) & _IOC_READ) 16519cac00b8f0079d5d3d54ec4dae453d58dec30e7cStefan Richter if (copy_to_user(arg, &buffer, _IOC_SIZE(cmd))) 16524f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg return -EFAULT; 16534f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg 16542dbd7d7e2327b0c2cc4e2de903e1cfa19980a504Stefan Richter return ret; 165519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg} 165619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 165753dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richterstatic long fw_device_op_ioctl(struct file *file, 165853dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richter unsigned int cmd, unsigned long arg) 165919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{ 166064582298b9c29535188380f488873e7d2196a2ebStefan Richter return dispatch_ioctl(file->private_data, cmd, (void __user *)arg); 166119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg} 166219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 166319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg#ifdef CONFIG_COMPAT 166453dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richterstatic long fw_device_op_compat_ioctl(struct file *file, 166553dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richter unsigned int cmd, unsigned long arg) 166619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{ 166764582298b9c29535188380f488873e7d2196a2ebStefan Richter return dispatch_ioctl(file->private_data, cmd, compat_ptr(arg)); 166819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg} 166919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg#endif 167019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 167119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsbergstatic int fw_device_op_mmap(struct file *file, struct vm_area_struct *vma) 167219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{ 167319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg struct client *client = file->private_data; 16749aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg unsigned long size; 16752dbd7d7e2327b0c2cc4e2de903e1cfa19980a504Stefan Richter int page_count, ret; 16769aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg 1677551f4cb9de716ffcdaf968c99a450c22ff12e8c3Jay Fenlason if (fw_device_is_shutdown(client->device)) 1678551f4cb9de716ffcdaf968c99a450c22ff12e8c3Jay Fenlason return -ENODEV; 1679551f4cb9de716ffcdaf968c99a450c22ff12e8c3Jay Fenlason 16809aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg /* FIXME: We could support multiple buffers, but we don't. */ 16819aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg if (client->buffer.pages != NULL) 16829aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg return -EBUSY; 16839aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg 16849aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg if (!(vma->vm_flags & VM_SHARED)) 16859aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg return -EINVAL; 168619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 16879aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg if (vma->vm_start & ~PAGE_MASK) 168819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg return -EINVAL; 168919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 169019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg client->vm_start = vma->vm_start; 16919aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg size = vma->vm_end - vma->vm_start; 16929aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg page_count = size >> PAGE_SHIFT; 16939aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg if (size & ~PAGE_MASK) 16949aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg return -EINVAL; 16959aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg 16960b6c4857f7684f6d3f59e0506f62953575346978Stefan Richter ret = fw_iso_buffer_alloc(&client->buffer, page_count); 16972dbd7d7e2327b0c2cc4e2de903e1cfa19980a504Stefan Richter if (ret < 0) 16982dbd7d7e2327b0c2cc4e2de903e1cfa19980a504Stefan Richter return ret; 169919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 17000b6c4857f7684f6d3f59e0506f62953575346978Stefan Richter spin_lock_irq(&client->lock); 17010b6c4857f7684f6d3f59e0506f62953575346978Stefan Richter if (client->iso_context) { 17020b6c4857f7684f6d3f59e0506f62953575346978Stefan Richter ret = fw_iso_buffer_map_dma(&client->buffer, 17030b6c4857f7684f6d3f59e0506f62953575346978Stefan Richter client->device->card, 17040b6c4857f7684f6d3f59e0506f62953575346978Stefan Richter iso_dma_direction(client->iso_context)); 17050b6c4857f7684f6d3f59e0506f62953575346978Stefan Richter client->buffer_is_mapped = (ret == 0); 17060b6c4857f7684f6d3f59e0506f62953575346978Stefan Richter } 17070b6c4857f7684f6d3f59e0506f62953575346978Stefan Richter spin_unlock_irq(&client->lock); 17082dbd7d7e2327b0c2cc4e2de903e1cfa19980a504Stefan Richter if (ret < 0) 17090b6c4857f7684f6d3f59e0506f62953575346978Stefan Richter goto fail; 17109aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg 17110b6c4857f7684f6d3f59e0506f62953575346978Stefan Richter ret = fw_iso_buffer_map_vma(&client->buffer, vma); 17120b6c4857f7684f6d3f59e0506f62953575346978Stefan Richter if (ret < 0) 17130b6c4857f7684f6d3f59e0506f62953575346978Stefan Richter goto fail; 17140b6c4857f7684f6d3f59e0506f62953575346978Stefan Richter 17150b6c4857f7684f6d3f59e0506f62953575346978Stefan Richter return 0; 17160b6c4857f7684f6d3f59e0506f62953575346978Stefan Richter fail: 17170b6c4857f7684f6d3f59e0506f62953575346978Stefan Richter fw_iso_buffer_destroy(&client->buffer, client->device->card); 17182dbd7d7e2327b0c2cc4e2de903e1cfa19980a504Stefan Richter return ret; 171919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg} 172019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 17215a5e62da9be255439e8ce59f96828775b7b33374Clemens Ladischstatic int is_outbound_transaction_resource(int id, void *p, void *data) 17225a5e62da9be255439e8ce59f96828775b7b33374Clemens Ladisch{ 17235a5e62da9be255439e8ce59f96828775b7b33374Clemens Ladisch struct client_resource *resource = p; 17245a5e62da9be255439e8ce59f96828775b7b33374Clemens Ladisch 17255a5e62da9be255439e8ce59f96828775b7b33374Clemens Ladisch return resource->release == release_transaction; 17265a5e62da9be255439e8ce59f96828775b7b33374Clemens Ladisch} 17275a5e62da9be255439e8ce59f96828775b7b33374Clemens Ladisch 17285a5e62da9be255439e8ce59f96828775b7b33374Clemens Ladischstatic int has_outbound_transactions(struct client *client) 17295a5e62da9be255439e8ce59f96828775b7b33374Clemens Ladisch{ 17305a5e62da9be255439e8ce59f96828775b7b33374Clemens Ladisch int ret; 17315a5e62da9be255439e8ce59f96828775b7b33374Clemens Ladisch 17325a5e62da9be255439e8ce59f96828775b7b33374Clemens Ladisch spin_lock_irq(&client->lock); 17335a5e62da9be255439e8ce59f96828775b7b33374Clemens Ladisch ret = idr_for_each(&client->resource_idr, 17345a5e62da9be255439e8ce59f96828775b7b33374Clemens Ladisch is_outbound_transaction_resource, NULL); 17355a5e62da9be255439e8ce59f96828775b7b33374Clemens Ladisch spin_unlock_irq(&client->lock); 17365a5e62da9be255439e8ce59f96828775b7b33374Clemens Ladisch 17375a5e62da9be255439e8ce59f96828775b7b33374Clemens Ladisch return ret; 17385a5e62da9be255439e8ce59f96828775b7b33374Clemens Ladisch} 17395a5e62da9be255439e8ce59f96828775b7b33374Clemens Ladisch 174045ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlasonstatic int shutdown_resource(int id, void *p, void *data) 174145ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason{ 1742e21fcf798e246202d7b60e864f1d7302ebaaf41cStefan Richter struct client_resource *resource = p; 174345ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason struct client *client = data; 174445ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason 1745e21fcf798e246202d7b60e864f1d7302ebaaf41cStefan Richter resource->release(client, resource); 1746fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter client_put(client); 174745ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason 174845ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason return 0; 174945ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason} 175045ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason 175119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsbergstatic int fw_device_op_release(struct inode *inode, struct file *file) 175219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{ 175319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg struct client *client = file->private_data; 1754e21fcf798e246202d7b60e864f1d7302ebaaf41cStefan Richter struct event *event, *next_event; 175519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 1756bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter spin_lock_irq(&client->device->card->lock); 1757bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter list_del(&client->phy_receiver_link); 1758bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter spin_unlock_irq(&client->device->card->lock); 1759bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter 176097811e347310766030a648fdf0e407b2c91a39c1Stefan Richter mutex_lock(&client->device->client_list_mutex); 176197811e347310766030a648fdf0e407b2c91a39c1Stefan Richter list_del(&client->link); 176297811e347310766030a648fdf0e407b2c91a39c1Stefan Richter mutex_unlock(&client->device->client_list_mutex); 176397811e347310766030a648fdf0e407b2c91a39c1Stefan Richter 176419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg if (client->iso_context) 176519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg fw_iso_context_destroy(client->iso_context); 176619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 176736a755cfc398fc50abc74055d4478c1b067dac55Stefan Richter if (client->buffer.pages) 176836a755cfc398fc50abc74055d4478c1b067dac55Stefan Richter fw_iso_buffer_destroy(&client->buffer, client->device->card); 176936a755cfc398fc50abc74055d4478c1b067dac55Stefan Richter 177045ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason /* Freeze client->resource_idr and client->event_list */ 17713ba949868a6dc082b24cba5c3bf3f50de7391433Stefan Richter spin_lock_irq(&client->lock); 177245ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason client->in_shutdown = true; 17733ba949868a6dc082b24cba5c3bf3f50de7391433Stefan Richter spin_unlock_irq(&client->lock); 177466dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg 17755a5e62da9be255439e8ce59f96828775b7b33374Clemens Ladisch wait_event(client->tx_flush_wait, !has_outbound_transactions(client)); 17765a5e62da9be255439e8ce59f96828775b7b33374Clemens Ladisch 177745ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason idr_for_each(&client->resource_idr, shutdown_resource, client); 177845ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason idr_destroy(&client->resource_idr); 177928cf6a04c82857d562968dc3a8a89726e6ac3dcbKristian Høgsberg 1780e21fcf798e246202d7b60e864f1d7302ebaaf41cStefan Richter list_for_each_entry_safe(event, next_event, &client->event_list, link) 1781e21fcf798e246202d7b60e864f1d7302ebaaf41cStefan Richter kfree(event); 178219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 1783fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter client_put(client); 178419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 178519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg return 0; 178619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg} 178719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 178819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsbergstatic unsigned int fw_device_op_poll(struct file *file, poll_table * pt) 178919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{ 179019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg struct client *client = file->private_data; 17912603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg unsigned int mask = 0; 179219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 179319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg poll_wait(file, &client->wait, pt); 179419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 17952603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg if (fw_device_is_shutdown(client->device)) 17962603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg mask |= POLLHUP | POLLERR; 179719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg if (!list_empty(&client->event_list)) 17982603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg mask |= POLLIN | POLLRDNORM; 17992603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg 18002603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg return mask; 180119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg} 180219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 180321ebcd1224d05c8673053e1e93ab9ec7ef3e0b84Stefan Richterconst struct file_operations fw_device_ops = { 180419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg .owner = THIS_MODULE, 18053ac26b2ee30005930117fe6a180c139c5f300fafStefan Richter .llseek = no_llseek, 180619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg .open = fw_device_op_open, 180719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg .read = fw_device_op_read, 180819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg .unlocked_ioctl = fw_device_op_ioctl, 180919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg .mmap = fw_device_op_mmap, 18103ac26b2ee30005930117fe6a180c139c5f300fafStefan Richter .release = fw_device_op_release, 18113ac26b2ee30005930117fe6a180c139c5f300fafStefan Richter .poll = fw_device_op_poll, 181219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg#ifdef CONFIG_COMPAT 18135af4e5eab30d481f76b89a2167c873dfad960acbStefan Richter .compat_ioctl = fw_device_op_compat_ioctl, 181419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg#endif 181519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg}; 1816