core-cdev.c revision 13882a82ee1646336c3996c93b4a560a55d2a419
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> 25be5bbd6756b44602a3f281af05c2f416fa9bd1c6Stefan Richter#include <linux/errno.h> 2677c9a5daa9c4d9b37812c9c69c7bcbb3f9399c3cStefan Richter#include <linux/firewire.h> 27be5bbd6756b44602a3f281af05c2f416fa9bd1c6Stefan Richter#include <linux/firewire-cdev.h> 28be5bbd6756b44602a3f281af05c2f416fa9bd1c6Stefan Richter#include <linux/idr.h> 294a9bde9b8ab55a2bb51b57cad215a97bcf80bae2Stefan Richter#include <linux/irqflags.h> 30b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter#include <linux/jiffies.h> 3119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg#include <linux/kernel.h> 32fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter#include <linux/kref.h> 33be5bbd6756b44602a3f281af05c2f416fa9bd1c6Stefan Richter#include <linux/mm.h> 34be5bbd6756b44602a3f281af05c2f416fa9bd1c6Stefan Richter#include <linux/module.h> 35d67cfb9613f373d76daa2c8d209629601424ca12Stefan Richter#include <linux/mutex.h> 3619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg#include <linux/poll.h> 37ae2a97661482c1d0f1aa41b837da95054d0e9a1bStefan Richter#include <linux/sched.h> /* required for linux/wait.h */ 385a0e3ad6af8660be21ca98a971cd00f331318c05Tejun Heo#include <linux/slab.h> 39cf417e5494582453c033d8cac9e1352e74215435Jay Fenlason#include <linux/spinlock.h> 40281e20323ab72180137824a298ee9e21e6f9acf6Stefan Richter#include <linux/string.h> 41be5bbd6756b44602a3f281af05c2f416fa9bd1c6Stefan Richter#include <linux/time.h> 42e034d242593f12533c11742ce38c245a33e57dc7Stefan Richter#include <linux/uaccess.h> 43be5bbd6756b44602a3f281af05c2f416fa9bd1c6Stefan Richter#include <linux/vmalloc.h> 44be5bbd6756b44602a3f281af05c2f416fa9bd1c6Stefan Richter#include <linux/wait.h> 45b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter#include <linux/workqueue.h> 46be5bbd6756b44602a3f281af05c2f416fa9bd1c6Stefan Richter 47a64408b96b5f67c2778958a230b5cfa3408a4a81Stefan Richter#include <asm/system.h> 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 */ 548e2b2b46ea4ca5ef790dddf78b360ed736a62d7cStefan Richter#define FW_CDEV_KERNEL_VERSION 4 558e2b2b46ea4ca5ef790dddf78b360ed736a62d7cStefan Richter#define FW_CDEV_VERSION_EVENT_REQUEST2 4 568e2b2b46ea4ca5ef790dddf78b360ed736a62d7cStefan Richter#define FW_CDEV_VERSION_ALLOCATE_REGION_END 4 57604f45167824e18ad5766e51ecf1d4d65f15118dStefan Richter 5819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsbergstruct client { 59344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg u32 version; 6019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg struct fw_device *device; 6145ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason 6219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg spinlock_t lock; 6345ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason bool in_shutdown; 6445ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason struct idr resource_idr; 6519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg struct list_head event_list; 6619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg wait_queue_head_t wait; 675a5e62da9be255439e8ce59f96828775b7b33374Clemens Ladisch wait_queue_head_t tx_flush_wait; 68da8ecffaed434a12930f652898f9e86d1c2abc3eKristian Høgsberg u64 bus_reset_closure; 699aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg 7019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg struct fw_iso_context *iso_context; 71abaa5743e340c23922d92c9a5a6753ea3ae71e58Kristian Høgsberg u64 iso_closure; 729aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg struct fw_iso_buffer buffer; 739aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg unsigned long vm_start; 7497bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg 75bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter struct list_head phy_receiver_link; 76bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter u64 phy_receiver_closure; 77bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter 7897bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg struct list_head link; 79fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter struct kref kref; 8019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg}; 8119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 82fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richterstatic inline void client_get(struct client *client) 83fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter{ 84fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter kref_get(&client->kref); 85fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter} 86fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter 87fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richterstatic void client_release(struct kref *kref) 88fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter{ 89fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter struct client *client = container_of(kref, struct client, kref); 90fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter 91fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter fw_device_put(client->device); 92fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter kfree(client); 93fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter} 94fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter 95fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richterstatic void client_put(struct client *client) 96fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter{ 97fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter kref_put(&client->kref, client_release); 98fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter} 99fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter 10097c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richterstruct client_resource; 10197c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richtertypedef void (*client_resource_release_fn_t)(struct client *, 10297c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter struct client_resource *); 10397c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richterstruct client_resource { 10497c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter client_resource_release_fn_t release; 10597c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter int handle; 10697c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter}; 10797c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter 10897c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richterstruct address_handler_resource { 10997c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter struct client_resource resource; 11097c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter struct fw_address_handler handler; 11197c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter __u64 closure; 11297c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter struct client *client; 11397c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter}; 11497c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter 11597c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richterstruct outbound_transaction_resource { 11697c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter struct client_resource resource; 11797c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter struct fw_transaction transaction; 11897c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter}; 11997c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter 12097c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richterstruct inbound_transaction_resource { 12197c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter struct client_resource resource; 12208bd34c98d631fe85744d4c920c80f48a1d95f54Jay Fenlason struct fw_card *card; 12397c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter struct fw_request *request; 12497c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter void *data; 12597c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter size_t length; 12697c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter}; 12797c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter 12897c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richterstruct descriptor_resource { 12997c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter struct client_resource resource; 13097c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter struct fw_descriptor descriptor; 13197c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter u32 data[0]; 13297c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter}; 13397c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter 134b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richterstruct iso_resource { 135b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter struct client_resource resource; 136b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter struct client *client; 137b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter /* Schedule work and access todo only with client->lock held. */ 138b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter struct delayed_work work; 1391ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter enum {ISO_RES_ALLOC, ISO_RES_REALLOC, ISO_RES_DEALLOC, 1401ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter ISO_RES_ALLOC_ONCE, ISO_RES_DEALLOC_ONCE,} todo; 141b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter int generation; 142b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter u64 channels; 143b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter s32 bandwidth; 144b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter struct iso_resource_event *e_alloc, *e_dealloc; 145b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter}; 146b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter 147b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richterstatic void release_iso_resource(struct client *, struct client_resource *); 148b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter 1499fb551bf72929b316abb6d96cfb2ec05e896042aStefan Richterstatic void schedule_iso_resource(struct iso_resource *r, unsigned long delay) 1509fb551bf72929b316abb6d96cfb2ec05e896042aStefan Richter{ 1519fb551bf72929b316abb6d96cfb2ec05e896042aStefan Richter client_get(r->client); 1529fb551bf72929b316abb6d96cfb2ec05e896042aStefan Richter if (!schedule_delayed_work(&r->work, delay)) 1539fb551bf72929b316abb6d96cfb2ec05e896042aStefan Richter client_put(r->client); 1549fb551bf72929b316abb6d96cfb2ec05e896042aStefan Richter} 1559fb551bf72929b316abb6d96cfb2ec05e896042aStefan Richter 1569fb551bf72929b316abb6d96cfb2ec05e896042aStefan Richterstatic void schedule_if_iso_resource(struct client_resource *resource) 1579fb551bf72929b316abb6d96cfb2ec05e896042aStefan Richter{ 1589fb551bf72929b316abb6d96cfb2ec05e896042aStefan Richter if (resource->release == release_iso_resource) 1599fb551bf72929b316abb6d96cfb2ec05e896042aStefan Richter schedule_iso_resource(container_of(resource, 1609fb551bf72929b316abb6d96cfb2ec05e896042aStefan Richter struct iso_resource, resource), 0); 1619fb551bf72929b316abb6d96cfb2ec05e896042aStefan Richter} 1629fb551bf72929b316abb6d96cfb2ec05e896042aStefan Richter 16397c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter/* 16497c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter * dequeue_event() just kfree()'s the event, so the event has to be 16597c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter * the first field in a struct XYZ_event. 16697c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter */ 16797c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richterstruct event { 16897c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter struct { void *data; size_t size; } v[2]; 16997c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter struct list_head link; 17097c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter}; 17197c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter 17297c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richterstruct bus_reset_event { 17397c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter struct event event; 17497c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter struct fw_cdev_event_bus_reset reset; 17597c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter}; 17697c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter 17797c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richterstruct outbound_transaction_event { 17897c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter struct event event; 17997c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter struct client *client; 18097c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter struct outbound_transaction_resource r; 18197c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter struct fw_cdev_event_response response; 18297c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter}; 18397c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter 18497c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richterstruct inbound_transaction_event { 18597c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter struct event event; 186e205597d188a9ea69ce43f740a14f07b3f5b996aStefan Richter union { 187e205597d188a9ea69ce43f740a14f07b3f5b996aStefan Richter struct fw_cdev_event_request request; 188e205597d188a9ea69ce43f740a14f07b3f5b996aStefan Richter struct fw_cdev_event_request2 request2; 189e205597d188a9ea69ce43f740a14f07b3f5b996aStefan Richter } req; 19097c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter}; 19197c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter 19297c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richterstruct iso_interrupt_event { 19397c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter struct event event; 19497c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter struct fw_cdev_event_iso_interrupt interrupt; 19597c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter}; 19697c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter 197872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richterstruct iso_interrupt_mc_event { 198872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter struct event event; 199872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter struct fw_cdev_event_iso_interrupt_mc interrupt; 200872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter}; 201872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter 202b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richterstruct iso_resource_event { 203b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter struct event event; 204e21fcf798e246202d7b60e864f1d7302ebaaf41cStefan Richter struct fw_cdev_event_iso_resource iso_resource; 205b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter}; 206b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter 207850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richterstruct outbound_phy_packet_event { 208850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter struct event event; 209850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter struct client *client; 210850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter struct fw_packet p; 211850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter struct fw_cdev_event_phy_packet phy_packet; 212850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter}; 213850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter 214bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richterstruct inbound_phy_packet_event { 215bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter struct event event; 216bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter struct fw_cdev_event_phy_packet phy_packet; 217bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter}; 218bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter 21953dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richterstatic inline void __user *u64_to_uptr(__u64 value) 22019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{ 22119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg return (void __user *)(unsigned long)value; 22219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg} 22319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 22453dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richterstatic inline __u64 uptr_to_u64(void __user *ptr) 22519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{ 22619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg return (__u64)(unsigned long)ptr; 22719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg} 22819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 22919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsbergstatic int fw_device_op_open(struct inode *inode, struct file *file) 23019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{ 23119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg struct fw_device *device; 23219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg struct client *client; 23319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 23496b19062e741b715cf399312c30e0672d8889569Stefan Richter device = fw_device_get_by_devt(inode->i_rdev); 235a3aca3dabbcf00f2088d472f27755c29acaa992eKristian Høgsberg if (device == NULL) 236a3aca3dabbcf00f2088d472f27755c29acaa992eKristian Høgsberg return -ENODEV; 23719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 238551f4cb9de716ffcdaf968c99a450c22ff12e8c3Jay Fenlason if (fw_device_is_shutdown(device)) { 239551f4cb9de716ffcdaf968c99a450c22ff12e8c3Jay Fenlason fw_device_put(device); 240551f4cb9de716ffcdaf968c99a450c22ff12e8c3Jay Fenlason return -ENODEV; 241551f4cb9de716ffcdaf968c99a450c22ff12e8c3Jay Fenlason } 242551f4cb9de716ffcdaf968c99a450c22ff12e8c3Jay Fenlason 2432d826cc5c791bdc5f5651324c485746be9492be0Kristian Høgsberg client = kzalloc(sizeof(*client), GFP_KERNEL); 24496b19062e741b715cf399312c30e0672d8889569Stefan Richter if (client == NULL) { 24596b19062e741b715cf399312c30e0672d8889569Stefan Richter fw_device_put(device); 24619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg return -ENOMEM; 24796b19062e741b715cf399312c30e0672d8889569Stefan Richter } 24819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 24996b19062e741b715cf399312c30e0672d8889569Stefan Richter client->device = device; 25019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg spin_lock_init(&client->lock); 25145ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason idr_init(&client->resource_idr); 25245ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason INIT_LIST_HEAD(&client->event_list); 25319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg init_waitqueue_head(&client->wait); 2545a5e62da9be255439e8ce59f96828775b7b33374Clemens Ladisch init_waitqueue_head(&client->tx_flush_wait); 255bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter INIT_LIST_HEAD(&client->phy_receiver_link); 256fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter kref_init(&client->kref); 25719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 25819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg file->private_data = client; 25919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 260d67cfb9613f373d76daa2c8d209629601424ca12Stefan Richter mutex_lock(&device->client_list_mutex); 26197bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg list_add_tail(&client->link, &device->client_list); 262d67cfb9613f373d76daa2c8d209629601424ca12Stefan Richter mutex_unlock(&device->client_list_mutex); 26397bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg 2643ac26b2ee30005930117fe6a180c139c5f300fafStefan Richter return nonseekable_open(inode, file); 26519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg} 26619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 26719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsbergstatic void queue_event(struct client *client, struct event *event, 26819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg void *data0, size_t size0, void *data1, size_t size1) 26919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{ 27019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg unsigned long flags; 27119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 27219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg event->v[0].data = data0; 27319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg event->v[0].size = size0; 27419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg event->v[1].data = data1; 27519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg event->v[1].size = size1; 27619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 27719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg spin_lock_irqsave(&client->lock, flags); 27845ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason if (client->in_shutdown) 27945ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason kfree(event); 28045ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason else 28145ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason list_add_tail(&event->link, &client->event_list); 28219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg spin_unlock_irqrestore(&client->lock, flags); 28383431cba3d847fc2296d3f38ce7feb623a1cfc45Jay Fenlason 28483431cba3d847fc2296d3f38ce7feb623a1cfc45Jay Fenlason wake_up_interruptible(&client->wait); 28519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg} 28619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 28753dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richterstatic int dequeue_event(struct client *client, 28853dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richter char __user *buffer, size_t count) 28919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{ 29019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg struct event *event; 29119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg size_t size, total; 2922dbd7d7e2327b0c2cc4e2de903e1cfa19980a504Stefan Richter int i, ret; 29319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 2942dbd7d7e2327b0c2cc4e2de903e1cfa19980a504Stefan Richter ret = wait_event_interruptible(client->wait, 2952dbd7d7e2327b0c2cc4e2de903e1cfa19980a504Stefan Richter !list_empty(&client->event_list) || 2962dbd7d7e2327b0c2cc4e2de903e1cfa19980a504Stefan Richter fw_device_is_shutdown(client->device)); 2972dbd7d7e2327b0c2cc4e2de903e1cfa19980a504Stefan Richter if (ret < 0) 2982dbd7d7e2327b0c2cc4e2de903e1cfa19980a504Stefan Richter return ret; 29919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 3002603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg if (list_empty(&client->event_list) && 3012603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg fw_device_is_shutdown(client->device)) 3022603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg return -ENODEV; 30319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 3043ba949868a6dc082b24cba5c3bf3f50de7391433Stefan Richter spin_lock_irq(&client->lock); 305a459b8ab9c176143fecef8ace4b70d6dbd7a8113Stefan Richter event = list_first_entry(&client->event_list, struct event, link); 30619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg list_del(&event->link); 3073ba949868a6dc082b24cba5c3bf3f50de7391433Stefan Richter spin_unlock_irq(&client->lock); 30819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 30919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg total = 0; 31019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg for (i = 0; i < ARRAY_SIZE(event->v) && total < count; i++) { 31119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg size = min(event->v[i].size, count - total); 3122603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg if (copy_to_user(buffer + total, event->v[i].data, size)) { 3132dbd7d7e2327b0c2cc4e2de903e1cfa19980a504Stefan Richter ret = -EFAULT; 31419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg goto out; 3152603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg } 31619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg total += size; 31719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg } 3182dbd7d7e2327b0c2cc4e2de903e1cfa19980a504Stefan Richter ret = total; 31919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 32019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg out: 32119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg kfree(event); 32219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 3232dbd7d7e2327b0c2cc4e2de903e1cfa19980a504Stefan Richter return ret; 32419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg} 32519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 32653dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richterstatic ssize_t fw_device_op_read(struct file *file, char __user *buffer, 32753dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richter size_t count, loff_t *offset) 32819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{ 32919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg struct client *client = file->private_data; 33019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 33119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg return dequeue_event(client, buffer, count); 33219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg} 33319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 33453dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richterstatic void fill_bus_reset_event(struct fw_cdev_event_bus_reset *event, 33553dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richter struct client *client) 336344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg{ 337da8ecffaed434a12930f652898f9e86d1c2abc3eKristian Høgsberg struct fw_card *card = client->device->card; 338cf417e5494582453c033d8cac9e1352e74215435Jay Fenlason 3393ba949868a6dc082b24cba5c3bf3f50de7391433Stefan Richter spin_lock_irq(&card->lock); 340344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg 341da8ecffaed434a12930f652898f9e86d1c2abc3eKristian Høgsberg event->closure = client->bus_reset_closure; 342344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg event->type = FW_CDEV_EVENT_BUS_RESET; 343cf5a56ac8083dd04ffe8b9b2ec7895e9bcff44bcStefan Richter event->generation = client->device->generation; 344da8ecffaed434a12930f652898f9e86d1c2abc3eKristian Høgsberg event->node_id = client->device->node_id; 345344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg event->local_node_id = card->local_node->node_id; 346250b2b6dd421c9f8844a867d2ac06e0661e0ad93Stefan Richter event->bm_node_id = card->bm_node_id; 347344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg event->irm_node_id = card->irm_node->node_id; 348344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg event->root_node_id = card->root_node->node_id; 349cf417e5494582453c033d8cac9e1352e74215435Jay Fenlason 3503ba949868a6dc082b24cba5c3bf3f50de7391433Stefan Richter spin_unlock_irq(&card->lock); 351344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg} 352344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg 35353dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richterstatic void for_each_client(struct fw_device *device, 35453dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richter void (*callback)(struct client *client)) 3552603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg{ 3562603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg struct client *c; 3572603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg 358d67cfb9613f373d76daa2c8d209629601424ca12Stefan Richter mutex_lock(&device->client_list_mutex); 3592603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg list_for_each_entry(c, &device->client_list, link) 3602603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg callback(c); 361d67cfb9613f373d76daa2c8d209629601424ca12Stefan Richter mutex_unlock(&device->client_list_mutex); 3622603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg} 3632603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg 364b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richterstatic int schedule_reallocations(int id, void *p, void *data) 365b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter{ 3669fb551bf72929b316abb6d96cfb2ec05e896042aStefan Richter schedule_if_iso_resource(p); 367b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter 368b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter return 0; 369b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter} 370b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter 37153dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richterstatic void queue_bus_reset_event(struct client *client) 37297bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg{ 37397c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter struct bus_reset_event *e; 37497bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg 37597c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter e = kzalloc(sizeof(*e), GFP_KERNEL); 37697c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter if (e == NULL) { 377bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter fw_notify("Out of memory when allocating event\n"); 37897bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg return; 37997bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg } 38097bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg 38197c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter fill_bus_reset_event(&e->reset, client); 38297bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg 38397c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter queue_event(client, &e->event, 38497c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter &e->reset, sizeof(e->reset), NULL, 0); 385b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter 386b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter spin_lock_irq(&client->lock); 387b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter idr_for_each(&client->resource_idr, schedule_reallocations, client); 388b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter spin_unlock_irq(&client->lock); 38997bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg} 39097bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg 39197bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsbergvoid fw_device_cdev_update(struct fw_device *device) 39297bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg{ 3932603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg for_each_client(device, queue_bus_reset_event); 3942603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg} 39597bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg 3962603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsbergstatic void wake_up_client(struct client *client) 3972603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg{ 3982603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg wake_up_interruptible(&client->wait); 3992603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg} 40097bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg 4012603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsbergvoid fw_device_cdev_remove(struct fw_device *device) 4022603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg{ 4032603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg for_each_client(device, wake_up_client); 40497bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg} 40597bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg 4066e95dea728f4af36c033fcf2318529bd46dae540Stefan Richterunion ioctl_arg { 4076e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter struct fw_cdev_get_info get_info; 4086e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter struct fw_cdev_send_request send_request; 4096e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter struct fw_cdev_allocate allocate; 4106e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter struct fw_cdev_deallocate deallocate; 4116e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter struct fw_cdev_send_response send_response; 4126e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter struct fw_cdev_initiate_bus_reset initiate_bus_reset; 4136e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter struct fw_cdev_add_descriptor add_descriptor; 4146e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter struct fw_cdev_remove_descriptor remove_descriptor; 4156e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter struct fw_cdev_create_iso_context create_iso_context; 4166e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter struct fw_cdev_queue_iso queue_iso; 4176e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter struct fw_cdev_start_iso start_iso; 4186e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter struct fw_cdev_stop_iso stop_iso; 4196e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter struct fw_cdev_get_cycle_timer get_cycle_timer; 4206e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter struct fw_cdev_allocate_iso_resource allocate_iso_resource; 4216e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter struct fw_cdev_send_stream_packet send_stream_packet; 4226e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter struct fw_cdev_get_cycle_timer2 get_cycle_timer2; 423850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter struct fw_cdev_send_phy_packet send_phy_packet; 424bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter struct fw_cdev_receive_phy_packets receive_phy_packets; 425872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter struct fw_cdev_set_iso_channels set_iso_channels; 4266e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter}; 4276e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter 4286e95dea728f4af36c033fcf2318529bd46dae540Stefan Richterstatic int ioctl_get_info(struct client *client, union ioctl_arg *arg) 42919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{ 4306e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter struct fw_cdev_get_info *a = &arg->get_info; 431344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg struct fw_cdev_event_bus_reset bus_reset; 432c9755e14a01987ada4063e8b4c50c2b6738d879eStefan Richter unsigned long ret = 0; 433344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg 4346e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter client->version = a->version; 435604f45167824e18ad5766e51ecf1d4d65f15118dStefan Richter a->version = FW_CDEV_KERNEL_VERSION; 4366e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter a->card = client->device->card->index; 437344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg 438c9755e14a01987ada4063e8b4c50c2b6738d879eStefan Richter down_read(&fw_device_rwsem); 439c9755e14a01987ada4063e8b4c50c2b6738d879eStefan Richter 4406e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter if (a->rom != 0) { 4416e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter size_t want = a->rom_length; 442d84702a5d7b500ead8db129ddea789c88764f357Stefan Richter size_t have = client->device->config_rom_length * 4; 443344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg 4446e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter ret = copy_to_user(u64_to_uptr(a->rom), 4456e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter client->device->config_rom, min(want, have)); 446344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg } 4476e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter a->rom_length = client->device->config_rom_length * 4; 448344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg 449c9755e14a01987ada4063e8b4c50c2b6738d879eStefan Richter up_read(&fw_device_rwsem); 450c9755e14a01987ada4063e8b4c50c2b6738d879eStefan Richter 451c9755e14a01987ada4063e8b4c50c2b6738d879eStefan Richter if (ret != 0) 452c9755e14a01987ada4063e8b4c50c2b6738d879eStefan Richter return -EFAULT; 453c9755e14a01987ada4063e8b4c50c2b6738d879eStefan Richter 4546e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter client->bus_reset_closure = a->bus_reset_closure; 4556e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter if (a->bus_reset != 0) { 456da8ecffaed434a12930f652898f9e86d1c2abc3eKristian Høgsberg fill_bus_reset_event(&bus_reset, client); 4576e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter if (copy_to_user(u64_to_uptr(a->bus_reset), 4586e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter &bus_reset, sizeof(bus_reset))) 459344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg return -EFAULT; 460344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg } 46119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 46219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg return 0; 46319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg} 46419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 46553dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richterstatic int add_client_resource(struct client *client, 46653dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richter struct client_resource *resource, gfp_t gfp_mask) 4673964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg{ 4683964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg unsigned long flags; 46945ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason int ret; 47045ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason 47145ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason retry: 47245ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason if (idr_pre_get(&client->resource_idr, gfp_mask) == 0) 47345ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason return -ENOMEM; 4743964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg 4753964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg spin_lock_irqsave(&client->lock, flags); 47645ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason if (client->in_shutdown) 47745ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason ret = -ECANCELED; 47845ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason else 47945ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason ret = idr_get_new(&client->resource_idr, resource, 48045ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason &resource->handle); 481b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter if (ret >= 0) { 482fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter client_get(client); 4839fb551bf72929b316abb6d96cfb2ec05e896042aStefan Richter schedule_if_iso_resource(resource); 484b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter } 4853964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg spin_unlock_irqrestore(&client->lock, flags); 48645ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason 48745ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason if (ret == -EAGAIN) 48845ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason goto retry; 48945ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason 49045ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason return ret < 0 ? ret : 0; 4913964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg} 4923964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg 49353dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richterstatic int release_client_resource(struct client *client, u32 handle, 49453dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richter client_resource_release_fn_t release, 495e21fcf798e246202d7b60e864f1d7302ebaaf41cStefan Richter struct client_resource **return_resource) 4963964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg{ 497e21fcf798e246202d7b60e864f1d7302ebaaf41cStefan Richter struct client_resource *resource; 4983964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg 4993ba949868a6dc082b24cba5c3bf3f50de7391433Stefan Richter spin_lock_irq(&client->lock); 50045ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason if (client->in_shutdown) 501e21fcf798e246202d7b60e864f1d7302ebaaf41cStefan Richter resource = NULL; 50245ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason else 503e21fcf798e246202d7b60e864f1d7302ebaaf41cStefan Richter resource = idr_find(&client->resource_idr, handle); 504e21fcf798e246202d7b60e864f1d7302ebaaf41cStefan Richter if (resource && resource->release == release) 50545ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason idr_remove(&client->resource_idr, handle); 5063ba949868a6dc082b24cba5c3bf3f50de7391433Stefan Richter spin_unlock_irq(&client->lock); 5073964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg 508e21fcf798e246202d7b60e864f1d7302ebaaf41cStefan Richter if (!(resource && resource->release == release)) 5093964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg return -EINVAL; 5103964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg 511e21fcf798e246202d7b60e864f1d7302ebaaf41cStefan Richter if (return_resource) 512e21fcf798e246202d7b60e864f1d7302ebaaf41cStefan Richter *return_resource = resource; 5133964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg else 514e21fcf798e246202d7b60e864f1d7302ebaaf41cStefan Richter resource->release(client, resource); 5153964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg 516fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter client_put(client); 517fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter 5183964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg return 0; 5193964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg} 5203964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg 52153dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richterstatic void release_transaction(struct client *client, 52253dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richter struct client_resource *resource) 5233964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg{ 5243964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg} 5253964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg 52653dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richterstatic void complete_transaction(struct fw_card *card, int rcode, 52753dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richter void *payload, size_t length, void *data) 52819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{ 52997c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter struct outbound_transaction_event *e = data; 53097c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter struct fw_cdev_event_response *rsp = &e->response; 53197c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter struct client *client = e->client; 53228cf6a04c82857d562968dc3a8a89726e6ac3dcbKristian Høgsberg unsigned long flags; 53319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 53497c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter if (length < rsp->length) 53597c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter rsp->length = length; 53619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg if (rcode == RCODE_COMPLETE) 53797c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter memcpy(rsp->data, payload, rsp->length); 53819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 53928cf6a04c82857d562968dc3a8a89726e6ac3dcbKristian Høgsberg spin_lock_irqsave(&client->lock, flags); 5405a5e62da9be255439e8ce59f96828775b7b33374Clemens Ladisch idr_remove(&client->resource_idr, e->r.resource.handle); 5415a5e62da9be255439e8ce59f96828775b7b33374Clemens Ladisch if (client->in_shutdown) 5425a5e62da9be255439e8ce59f96828775b7b33374Clemens Ladisch wake_up(&client->tx_flush_wait); 54328cf6a04c82857d562968dc3a8a89726e6ac3dcbKristian Høgsberg spin_unlock_irqrestore(&client->lock, flags); 54428cf6a04c82857d562968dc3a8a89726e6ac3dcbKristian Høgsberg 54597c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter rsp->type = FW_CDEV_EVENT_RESPONSE; 54697c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter rsp->rcode = rcode; 5478401d92ba46a1e859464cbd9c9ee304f6e361da3David Moore 5488401d92ba46a1e859464cbd9c9ee304f6e361da3David Moore /* 54997c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter * In the case that sizeof(*rsp) doesn't align with the position of the 5508401d92ba46a1e859464cbd9c9ee304f6e361da3David Moore * data, and the read is short, preserve an extra copy of the data 5518401d92ba46a1e859464cbd9c9ee304f6e361da3David Moore * to stay compatible with a pre-2.6.27 bug. Since the bug is harmless 5528401d92ba46a1e859464cbd9c9ee304f6e361da3David Moore * for short reads and some apps depended on it, this is both safe 5538401d92ba46a1e859464cbd9c9ee304f6e361da3David Moore * and prudent for compatibility. 5548401d92ba46a1e859464cbd9c9ee304f6e361da3David Moore */ 55597c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter if (rsp->length <= sizeof(*rsp) - offsetof(typeof(*rsp), data)) 55697c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter queue_event(client, &e->event, rsp, sizeof(*rsp), 55797c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter rsp->data, rsp->length); 5588401d92ba46a1e859464cbd9c9ee304f6e361da3David Moore else 55997c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter queue_event(client, &e->event, rsp, sizeof(*rsp) + rsp->length, 5608401d92ba46a1e859464cbd9c9ee304f6e361da3David Moore NULL, 0); 561fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter 5625a5e62da9be255439e8ce59f96828775b7b33374Clemens Ladisch /* Drop the idr's reference */ 5635a5e62da9be255439e8ce59f96828775b7b33374Clemens Ladisch client_put(client); 56419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg} 56519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 566acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richterstatic int init_request(struct client *client, 567acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter struct fw_cdev_send_request *request, 568acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter int destination_id, int speed) 56919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{ 57097c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter struct outbound_transaction_event *e; 5711f3125af8ed7410cc0ebcc0acd59bbfc1ae0057aStefan Richter int ret; 57219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 57318e9b10fcdc090d3a38606958167d5923c7099b7Stefan Richter if (request->tcode != TCODE_STREAM_DATA && 57418e9b10fcdc090d3a38606958167d5923c7099b7Stefan Richter (request->length > 4096 || request->length > 512 << speed)) 5755d3fd692a7196a9045fb606f891f5987959b65a0Stefan Richter return -EIO; 57619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 577a8e93f3dccc066cd6dd1e9db1e35942914fc57d1Clemens Ladisch if (request->tcode == TCODE_WRITE_QUADLET_REQUEST && 578a8e93f3dccc066cd6dd1e9db1e35942914fc57d1Clemens Ladisch request->length < 4) 579a8e93f3dccc066cd6dd1e9db1e35942914fc57d1Clemens Ladisch return -EINVAL; 580a8e93f3dccc066cd6dd1e9db1e35942914fc57d1Clemens Ladisch 58197c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter e = kmalloc(sizeof(*e) + request->length, GFP_KERNEL); 58297c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter if (e == NULL) 58319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg return -ENOMEM; 58419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 58597c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter e->client = client; 58697c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter e->response.length = request->length; 58797c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter e->response.closure = request->closure; 58819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 5894f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg if (request->data && 59097c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter copy_from_user(e->response.data, 5914f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg u64_to_uptr(request->data), request->length)) { 5921f3125af8ed7410cc0ebcc0acd59bbfc1ae0057aStefan Richter ret = -EFAULT; 59345ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason goto failed; 5941f3125af8ed7410cc0ebcc0acd59bbfc1ae0057aStefan Richter } 5951f3125af8ed7410cc0ebcc0acd59bbfc1ae0057aStefan Richter 59697c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter e->r.resource.release = release_transaction; 59797c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter ret = add_client_resource(client, &e->r.resource, GFP_KERNEL); 59845ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason if (ret < 0) 59945ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason goto failed; 60028cf6a04c82857d562968dc3a8a89726e6ac3dcbKristian Høgsberg 601acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter fw_send_request(client->device->card, &e->r.transaction, 602664d8010b170ae8b3ce9268b4f4da934d27b0491Stefan Richter request->tcode, destination_id, request->generation, 603664d8010b170ae8b3ce9268b4f4da934d27b0491Stefan Richter speed, request->offset, e->response.data, 604664d8010b170ae8b3ce9268b4f4da934d27b0491Stefan Richter request->length, complete_transaction, e); 605664d8010b170ae8b3ce9268b4f4da934d27b0491Stefan Richter return 0; 60619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 60745ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason failed: 60897c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter kfree(e); 6091f3125af8ed7410cc0ebcc0acd59bbfc1ae0057aStefan Richter 6101f3125af8ed7410cc0ebcc0acd59bbfc1ae0057aStefan Richter return ret; 61119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg} 61219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 6136e95dea728f4af36c033fcf2318529bd46dae540Stefan Richterstatic int ioctl_send_request(struct client *client, union ioctl_arg *arg) 614acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter{ 6156e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter switch (arg->send_request.tcode) { 616acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter case TCODE_WRITE_QUADLET_REQUEST: 617acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter case TCODE_WRITE_BLOCK_REQUEST: 618acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter case TCODE_READ_QUADLET_REQUEST: 619acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter case TCODE_READ_BLOCK_REQUEST: 620acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter case TCODE_LOCK_MASK_SWAP: 621acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter case TCODE_LOCK_COMPARE_SWAP: 622acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter case TCODE_LOCK_FETCH_ADD: 623acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter case TCODE_LOCK_LITTLE_ADD: 624acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter case TCODE_LOCK_BOUNDED_ADD: 625acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter case TCODE_LOCK_WRAP_ADD: 626acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter case TCODE_LOCK_VENDOR_DEPENDENT: 627acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter break; 628acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter default: 629acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter return -EINVAL; 630acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter } 631acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter 6326e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter return init_request(client, &arg->send_request, client->device->node_id, 633acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter client->device->max_speed); 634acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter} 635acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter 636281e20323ab72180137824a298ee9e21e6f9acf6Stefan Richterstatic inline bool is_fcp_request(struct fw_request *request) 637281e20323ab72180137824a298ee9e21e6f9acf6Stefan Richter{ 638281e20323ab72180137824a298ee9e21e6f9acf6Stefan Richter return request == NULL; 639281e20323ab72180137824a298ee9e21e6f9acf6Stefan Richter} 640281e20323ab72180137824a298ee9e21e6f9acf6Stefan Richter 64153dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richterstatic void release_request(struct client *client, 64253dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richter struct client_resource *resource) 6433964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg{ 64497c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter struct inbound_transaction_resource *r = container_of(resource, 64597c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter struct inbound_transaction_resource, resource); 6463964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg 647281e20323ab72180137824a298ee9e21e6f9acf6Stefan Richter if (is_fcp_request(r->request)) 648281e20323ab72180137824a298ee9e21e6f9acf6Stefan Richter kfree(r->data); 649281e20323ab72180137824a298ee9e21e6f9acf6Stefan Richter else 65008bd34c98d631fe85744d4c920c80f48a1d95f54Jay Fenlason fw_send_response(r->card, r->request, RCODE_CONFLICT_ERROR); 6510244f57302f7e8bebd2f1ab58767eac2e9f678a6Stefan Richter 6520244f57302f7e8bebd2f1ab58767eac2e9f678a6Stefan Richter fw_card_put(r->card); 65397c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter kfree(r); 6543964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg} 6553964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg 65697c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richterstatic void handle_request(struct fw_card *card, struct fw_request *request, 65753dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richter int tcode, int destination, int source, 65833e553fe2b4a983ef34a57ab1440d8d33397bb12Stefan Richter int generation, unsigned long long offset, 65953dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richter void *payload, size_t length, void *callback_data) 66019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{ 66197c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter struct address_handler_resource *handler = callback_data; 66297c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter struct inbound_transaction_resource *r; 66397c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter struct inbound_transaction_event *e; 664e205597d188a9ea69ce43f740a14f07b3f5b996aStefan Richter size_t event_size0; 665281e20323ab72180137824a298ee9e21e6f9acf6Stefan Richter void *fcp_frame = NULL; 66645ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason int ret; 66719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 6680244f57302f7e8bebd2f1ab58767eac2e9f678a6Stefan Richter /* card may be different from handler->client->device->card */ 6690244f57302f7e8bebd2f1ab58767eac2e9f678a6Stefan Richter fw_card_get(card); 6700244f57302f7e8bebd2f1ab58767eac2e9f678a6Stefan Richter 67197c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter r = kmalloc(sizeof(*r), GFP_ATOMIC); 6722d826cc5c791bdc5f5651324c485746be9492be0Kristian Høgsberg e = kmalloc(sizeof(*e), GFP_ATOMIC); 673bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter if (r == NULL || e == NULL) { 674bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter fw_notify("Out of memory when allocating event\n"); 67545ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason goto failed; 676bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter } 67708bd34c98d631fe85744d4c920c80f48a1d95f54Jay Fenlason r->card = card; 67897c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter r->request = request; 67997c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter r->data = payload; 68097c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter r->length = length; 68119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 682281e20323ab72180137824a298ee9e21e6f9acf6Stefan Richter if (is_fcp_request(request)) { 683281e20323ab72180137824a298ee9e21e6f9acf6Stefan Richter /* 684281e20323ab72180137824a298ee9e21e6f9acf6Stefan Richter * FIXME: Let core-transaction.c manage a 685281e20323ab72180137824a298ee9e21e6f9acf6Stefan Richter * single reference-counted copy? 686281e20323ab72180137824a298ee9e21e6f9acf6Stefan Richter */ 687281e20323ab72180137824a298ee9e21e6f9acf6Stefan Richter fcp_frame = kmemdup(payload, length, GFP_ATOMIC); 688281e20323ab72180137824a298ee9e21e6f9acf6Stefan Richter if (fcp_frame == NULL) 689281e20323ab72180137824a298ee9e21e6f9acf6Stefan Richter goto failed; 690281e20323ab72180137824a298ee9e21e6f9acf6Stefan Richter 691281e20323ab72180137824a298ee9e21e6f9acf6Stefan Richter r->data = fcp_frame; 692281e20323ab72180137824a298ee9e21e6f9acf6Stefan Richter } 693281e20323ab72180137824a298ee9e21e6f9acf6Stefan Richter 69497c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter r->resource.release = release_request; 69597c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter ret = add_client_resource(handler->client, &r->resource, GFP_ATOMIC); 69645ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason if (ret < 0) 69745ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason goto failed; 69819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 699e205597d188a9ea69ce43f740a14f07b3f5b996aStefan Richter if (handler->client->version < FW_CDEV_VERSION_EVENT_REQUEST2) { 700e205597d188a9ea69ce43f740a14f07b3f5b996aStefan Richter struct fw_cdev_event_request *req = &e->req.request; 701e205597d188a9ea69ce43f740a14f07b3f5b996aStefan Richter 702e205597d188a9ea69ce43f740a14f07b3f5b996aStefan Richter if (tcode & 0x10) 703e205597d188a9ea69ce43f740a14f07b3f5b996aStefan Richter tcode = TCODE_LOCK_REQUEST; 704e205597d188a9ea69ce43f740a14f07b3f5b996aStefan Richter 705e205597d188a9ea69ce43f740a14f07b3f5b996aStefan Richter req->type = FW_CDEV_EVENT_REQUEST; 706e205597d188a9ea69ce43f740a14f07b3f5b996aStefan Richter req->tcode = tcode; 707e205597d188a9ea69ce43f740a14f07b3f5b996aStefan Richter req->offset = offset; 708e205597d188a9ea69ce43f740a14f07b3f5b996aStefan Richter req->length = length; 709e205597d188a9ea69ce43f740a14f07b3f5b996aStefan Richter req->handle = r->resource.handle; 710e205597d188a9ea69ce43f740a14f07b3f5b996aStefan Richter req->closure = handler->closure; 711e205597d188a9ea69ce43f740a14f07b3f5b996aStefan Richter event_size0 = sizeof(*req); 712e205597d188a9ea69ce43f740a14f07b3f5b996aStefan Richter } else { 713e205597d188a9ea69ce43f740a14f07b3f5b996aStefan Richter struct fw_cdev_event_request2 *req = &e->req.request2; 714e205597d188a9ea69ce43f740a14f07b3f5b996aStefan Richter 715e205597d188a9ea69ce43f740a14f07b3f5b996aStefan Richter req->type = FW_CDEV_EVENT_REQUEST2; 716e205597d188a9ea69ce43f740a14f07b3f5b996aStefan Richter req->tcode = tcode; 717e205597d188a9ea69ce43f740a14f07b3f5b996aStefan Richter req->offset = offset; 718e205597d188a9ea69ce43f740a14f07b3f5b996aStefan Richter req->source_node_id = source; 719e205597d188a9ea69ce43f740a14f07b3f5b996aStefan Richter req->destination_node_id = destination; 720e205597d188a9ea69ce43f740a14f07b3f5b996aStefan Richter req->card = card->index; 721e205597d188a9ea69ce43f740a14f07b3f5b996aStefan Richter req->generation = generation; 722e205597d188a9ea69ce43f740a14f07b3f5b996aStefan Richter req->length = length; 723e205597d188a9ea69ce43f740a14f07b3f5b996aStefan Richter req->handle = r->resource.handle; 724e205597d188a9ea69ce43f740a14f07b3f5b996aStefan Richter req->closure = handler->closure; 725e205597d188a9ea69ce43f740a14f07b3f5b996aStefan Richter event_size0 = sizeof(*req); 726e205597d188a9ea69ce43f740a14f07b3f5b996aStefan Richter } 72719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 72897c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter queue_event(handler->client, &e->event, 729e205597d188a9ea69ce43f740a14f07b3f5b996aStefan Richter &e->req, event_size0, r->data, length); 73045ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason return; 73145ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason 73245ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason failed: 73397c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter kfree(r); 73445ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason kfree(e); 735281e20323ab72180137824a298ee9e21e6f9acf6Stefan Richter kfree(fcp_frame); 736281e20323ab72180137824a298ee9e21e6f9acf6Stefan Richter 737281e20323ab72180137824a298ee9e21e6f9acf6Stefan Richter if (!is_fcp_request(request)) 738db5d247ae811f49185a71e703b65acad845e4b18Clemens Ladisch fw_send_response(card, request, RCODE_CONFLICT_ERROR); 7390244f57302f7e8bebd2f1ab58767eac2e9f678a6Stefan Richter 7400244f57302f7e8bebd2f1ab58767eac2e9f678a6Stefan Richter fw_card_put(card); 74119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg} 74219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 74353dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richterstatic void release_address_handler(struct client *client, 74453dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richter struct client_resource *resource) 7453964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg{ 74697c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter struct address_handler_resource *r = 74797c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter container_of(resource, struct address_handler_resource, resource); 7483964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg 74997c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter fw_core_remove_address_handler(&r->handler); 75097c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter kfree(r); 7513964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg} 7523964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg 7536e95dea728f4af36c033fcf2318529bd46dae540Stefan Richterstatic int ioctl_allocate(struct client *client, union ioctl_arg *arg) 75419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{ 7556e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter struct fw_cdev_allocate *a = &arg->allocate; 75697c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter struct address_handler_resource *r; 75719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg struct fw_address_region region; 75845ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason int ret; 75919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 76097c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter r = kmalloc(sizeof(*r), GFP_KERNEL); 76197c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter if (r == NULL) 76219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg return -ENOMEM; 76319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 7646e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter region.start = a->offset; 7658e2b2b46ea4ca5ef790dddf78b360ed736a62d7cStefan Richter if (client->version < FW_CDEV_VERSION_ALLOCATE_REGION_END) 7668e2b2b46ea4ca5ef790dddf78b360ed736a62d7cStefan Richter region.end = a->offset + a->length; 7678e2b2b46ea4ca5ef790dddf78b360ed736a62d7cStefan Richter else 7688e2b2b46ea4ca5ef790dddf78b360ed736a62d7cStefan Richter region.end = a->region_end; 7698e2b2b46ea4ca5ef790dddf78b360ed736a62d7cStefan Richter 7706e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter r->handler.length = a->length; 77197c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter r->handler.address_callback = handle_request; 7726e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter r->handler.callback_data = r; 7736e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter r->closure = a->closure; 7746e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter r->client = client; 77519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 77697c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter ret = fw_core_add_address_handler(&r->handler, ®ion); 7773e0b5f0d7cb5fef402517e41eebff5a0f0e65a13Stefan Richter if (ret < 0) { 77897c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter kfree(r); 7793e0b5f0d7cb5fef402517e41eebff5a0f0e65a13Stefan Richter return ret; 78019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg } 7818e2b2b46ea4ca5ef790dddf78b360ed736a62d7cStefan Richter a->offset = r->handler.offset; 78219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 78397c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter r->resource.release = release_address_handler; 78497c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter ret = add_client_resource(client, &r->resource, GFP_KERNEL); 78545ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason if (ret < 0) { 78697c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter release_address_handler(client, &r->resource); 78745ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason return ret; 78845ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason } 7896e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter a->handle = r->resource.handle; 79019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 79119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg return 0; 79219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg} 79319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 7946e95dea728f4af36c033fcf2318529bd46dae540Stefan Richterstatic int ioctl_deallocate(struct client *client, union ioctl_arg *arg) 7959472316b6eab3500ded544f6e86700c33541ef4eKristian Høgsberg{ 7966e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter return release_client_resource(client, arg->deallocate.handle, 79745ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason release_address_handler, NULL); 7989472316b6eab3500ded544f6e86700c33541ef4eKristian Høgsberg} 7999472316b6eab3500ded544f6e86700c33541ef4eKristian Høgsberg 8006e95dea728f4af36c033fcf2318529bd46dae540Stefan Richterstatic int ioctl_send_response(struct client *client, union ioctl_arg *arg) 80119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{ 8026e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter struct fw_cdev_send_response *a = &arg->send_response; 8033964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg struct client_resource *resource; 80497c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter struct inbound_transaction_resource *r; 8057e44c0b56b07a5e34de9943cfb2fee72e71a9f0eStefan Richter int ret = 0; 80619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 8076e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter if (release_client_resource(client, a->handle, 80845ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason release_request, &resource) < 0) 80919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg return -EINVAL; 81045ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason 81197c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter r = container_of(resource, struct inbound_transaction_resource, 81297c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter resource); 813281e20323ab72180137824a298ee9e21e6f9acf6Stefan Richter if (is_fcp_request(r->request)) 814281e20323ab72180137824a298ee9e21e6f9acf6Stefan Richter goto out; 815281e20323ab72180137824a298ee9e21e6f9acf6Stefan Richter 816a10c0ce76098857b899505d05de9f2e13ddf7a7aClemens Ladisch if (a->length != fw_get_response_length(r->request)) { 817a10c0ce76098857b899505d05de9f2e13ddf7a7aClemens Ladisch ret = -EINVAL; 818a10c0ce76098857b899505d05de9f2e13ddf7a7aClemens Ladisch kfree(r->request); 819a10c0ce76098857b899505d05de9f2e13ddf7a7aClemens Ladisch goto out; 820a10c0ce76098857b899505d05de9f2e13ddf7a7aClemens Ladisch } 821a10c0ce76098857b899505d05de9f2e13ddf7a7aClemens Ladisch if (copy_from_user(r->data, u64_to_uptr(a->data), a->length)) { 822281e20323ab72180137824a298ee9e21e6f9acf6Stefan Richter ret = -EFAULT; 823281e20323ab72180137824a298ee9e21e6f9acf6Stefan Richter kfree(r->request); 824281e20323ab72180137824a298ee9e21e6f9acf6Stefan Richter goto out; 8257e44c0b56b07a5e34de9943cfb2fee72e71a9f0eStefan Richter } 82608bd34c98d631fe85744d4c920c80f48a1d95f54Jay Fenlason fw_send_response(r->card, r->request, a->rcode); 8277e44c0b56b07a5e34de9943cfb2fee72e71a9f0eStefan Richter out: 8280244f57302f7e8bebd2f1ab58767eac2e9f678a6Stefan Richter fw_card_put(r->card); 82919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg kfree(r); 83019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 8317e44c0b56b07a5e34de9943cfb2fee72e71a9f0eStefan Richter return ret; 83219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg} 83319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 8346e95dea728f4af36c033fcf2318529bd46dae540Stefan Richterstatic int ioctl_initiate_bus_reset(struct client *client, union ioctl_arg *arg) 8355371842b723dd04df57171f2c74660966901380cKristian Høgsberg{ 83602d37bed188c500ee7afb0a2dc6b65a80704c58eStefan Richter fw_schedule_bus_reset(client->device->card, true, 8376e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter arg->initiate_bus_reset.type == FW_CDEV_SHORT_RESET); 83802d37bed188c500ee7afb0a2dc6b65a80704c58eStefan Richter return 0; 8395371842b723dd04df57171f2c74660966901380cKristian Høgsberg} 8405371842b723dd04df57171f2c74660966901380cKristian Høgsberg 8413964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsbergstatic void release_descriptor(struct client *client, 8423964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg struct client_resource *resource) 8433964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg{ 84497c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter struct descriptor_resource *r = 84597c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter container_of(resource, struct descriptor_resource, resource); 8463964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg 84797c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter fw_core_remove_descriptor(&r->descriptor); 84897c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter kfree(r); 8493964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg} 8503964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg 8516e95dea728f4af36c033fcf2318529bd46dae540Stefan Richterstatic int ioctl_add_descriptor(struct client *client, union ioctl_arg *arg) 85266dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg{ 8536e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter struct fw_cdev_add_descriptor *a = &arg->add_descriptor; 85497c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter struct descriptor_resource *r; 85545ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason int ret; 85666dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg 857de487da8ca5839d057e1f4b57ee3f387e180b800Stefan Richter /* Access policy: Allow this ioctl only on local nodes' device files. */ 85892368890d551794ee8d7e90477d8498bb7f82a9bStefan Richter if (!client->device->is_local) 859de487da8ca5839d057e1f4b57ee3f387e180b800Stefan Richter return -ENOSYS; 860de487da8ca5839d057e1f4b57ee3f387e180b800Stefan Richter 8616e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter if (a->length > 256) 86266dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg return -EINVAL; 86366dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg 8646e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter r = kmalloc(sizeof(*r) + a->length * 4, GFP_KERNEL); 86597c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter if (r == NULL) 86666dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg return -ENOMEM; 86766dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg 8686e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter if (copy_from_user(r->data, u64_to_uptr(a->data), a->length * 4)) { 86945ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason ret = -EFAULT; 87045ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason goto failed; 87166dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg } 87266dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg 8736e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter r->descriptor.length = a->length; 8746e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter r->descriptor.immediate = a->immediate; 8756e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter r->descriptor.key = a->key; 87697c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter r->descriptor.data = r->data; 87766dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg 87897c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter ret = fw_core_add_descriptor(&r->descriptor); 87945ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason if (ret < 0) 88045ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason goto failed; 88166dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg 88297c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter r->resource.release = release_descriptor; 88397c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter ret = add_client_resource(client, &r->resource, GFP_KERNEL); 88445ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason if (ret < 0) { 88597c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter fw_core_remove_descriptor(&r->descriptor); 88645ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason goto failed; 88745ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason } 8886e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter a->handle = r->resource.handle; 88966dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg 89066dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg return 0; 89145ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason failed: 89297c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter kfree(r); 89345ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason 89445ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason return ret; 89566dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg} 89666dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg 8976e95dea728f4af36c033fcf2318529bd46dae540Stefan Richterstatic int ioctl_remove_descriptor(struct client *client, union ioctl_arg *arg) 89866dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg{ 8996e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter return release_client_resource(client, arg->remove_descriptor.handle, 90045ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason release_descriptor, NULL); 90166dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg} 90266dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg 90353dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richterstatic void iso_callback(struct fw_iso_context *context, u32 cycle, 90453dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richter size_t header_length, void *header, void *data) 90519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{ 90619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg struct client *client = data; 90797c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter struct iso_interrupt_event *e; 90819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 90956d04cb189f955e5167c27944d61aa57ad69b598Stefan Richter e = kmalloc(sizeof(*e) + header_length, GFP_ATOMIC); 910bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter if (e == NULL) { 911bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter fw_notify("Out of memory when allocating event\n"); 91219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg return; 913bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter } 91497c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter e->interrupt.type = FW_CDEV_EVENT_ISO_INTERRUPT; 91597c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter e->interrupt.closure = client->iso_closure; 91697c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter e->interrupt.cycle = cycle; 91797c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter e->interrupt.header_length = header_length; 91897c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter memcpy(e->interrupt.header, header, header_length); 91997c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter queue_event(client, &e->event, &e->interrupt, 92097c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter sizeof(e->interrupt) + header_length, NULL, 0); 92119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg} 92219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 923872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richterstatic void iso_mc_callback(struct fw_iso_context *context, 924872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter dma_addr_t completed, void *data) 925872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter{ 926872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter struct client *client = data; 927872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter struct iso_interrupt_mc_event *e; 928872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter 929872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter e = kmalloc(sizeof(*e), GFP_ATOMIC); 930872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter if (e == NULL) { 931872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter fw_notify("Out of memory when allocating event\n"); 932872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter return; 933872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter } 934872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter e->interrupt.type = FW_CDEV_EVENT_ISO_INTERRUPT_MULTICHANNEL; 935872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter e->interrupt.closure = client->iso_closure; 936872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter e->interrupt.completed = fw_iso_buffer_lookup(&client->buffer, 937872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter completed); 938872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter queue_event(client, &e->event, &e->interrupt, 939872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter sizeof(e->interrupt), NULL, 0); 940872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter} 941872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter 9426e95dea728f4af36c033fcf2318529bd46dae540Stefan Richterstatic int ioctl_create_iso_context(struct client *client, union ioctl_arg *arg) 94319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{ 9446e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter struct fw_cdev_create_iso_context *a = &arg->create_iso_context; 94524315c5e6f508edd84e996d67daef3d1bcc72f8bKristian Høgsberg struct fw_iso_context *context; 946872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter fw_iso_callback_t cb; 94719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 948eb5b35a560510efc6bb62f05c3c82e9596cdfafeStefan Richter BUILD_BUG_ON(FW_CDEV_ISO_CONTEXT_TRANSMIT != FW_ISO_CONTEXT_TRANSMIT || 949872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter FW_CDEV_ISO_CONTEXT_RECEIVE != FW_ISO_CONTEXT_RECEIVE || 950872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter FW_CDEV_ISO_CONTEXT_RECEIVE_MULTICHANNEL != 951872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter FW_ISO_CONTEXT_RECEIVE_MULTICHANNEL); 95221efb3cfc6ed49991638000f58bb23b838c76e25Kristian Høgsberg 9536e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter switch (a->type) { 954872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter case FW_ISO_CONTEXT_TRANSMIT: 955872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter if (a->speed > SCODE_3200 || a->channel > 63) 956c70dc788fd8d3870b41231b6a53a64afb98cfd13Kristian Høgsberg return -EINVAL; 957872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter 958872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter cb = iso_callback; 959c70dc788fd8d3870b41231b6a53a64afb98cfd13Kristian Høgsberg break; 960c70dc788fd8d3870b41231b6a53a64afb98cfd13Kristian Høgsberg 961872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter case FW_ISO_CONTEXT_RECEIVE: 962872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter if (a->header_size < 4 || (a->header_size & 3) || 963872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter a->channel > 63) 964c70dc788fd8d3870b41231b6a53a64afb98cfd13Kristian Høgsberg return -EINVAL; 965872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter 966872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter cb = iso_callback; 967872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter break; 968872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter 969872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter case FW_ISO_CONTEXT_RECEIVE_MULTICHANNEL: 970872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter cb = (fw_iso_callback_t)iso_mc_callback; 971c70dc788fd8d3870b41231b6a53a64afb98cfd13Kristian Høgsberg break; 972c70dc788fd8d3870b41231b6a53a64afb98cfd13Kristian Høgsberg 973c70dc788fd8d3870b41231b6a53a64afb98cfd13Kristian Høgsberg default: 97421efb3cfc6ed49991638000f58bb23b838c76e25Kristian Høgsberg return -EINVAL; 975c70dc788fd8d3870b41231b6a53a64afb98cfd13Kristian Høgsberg } 976c70dc788fd8d3870b41231b6a53a64afb98cfd13Kristian Høgsberg 9776e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter context = fw_iso_context_create(client->device->card, a->type, 978872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter a->channel, a->speed, a->header_size, cb, client); 97924315c5e6f508edd84e996d67daef3d1bcc72f8bKristian Høgsberg if (IS_ERR(context)) 98024315c5e6f508edd84e996d67daef3d1bcc72f8bKristian Høgsberg return PTR_ERR(context); 98124315c5e6f508edd84e996d67daef3d1bcc72f8bKristian Høgsberg 982bdfe273ee54b29498851fc8058516037d284270cClemens Ladisch /* We only support one context at this time. */ 983bdfe273ee54b29498851fc8058516037d284270cClemens Ladisch spin_lock_irq(&client->lock); 984bdfe273ee54b29498851fc8058516037d284270cClemens Ladisch if (client->iso_context != NULL) { 985bdfe273ee54b29498851fc8058516037d284270cClemens Ladisch spin_unlock_irq(&client->lock); 986bdfe273ee54b29498851fc8058516037d284270cClemens Ladisch fw_iso_context_destroy(context); 987bdfe273ee54b29498851fc8058516037d284270cClemens Ladisch return -EBUSY; 988bdfe273ee54b29498851fc8058516037d284270cClemens Ladisch } 9896e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter client->iso_closure = a->closure; 99024315c5e6f508edd84e996d67daef3d1bcc72f8bKristian Høgsberg client->iso_context = context; 991bdfe273ee54b29498851fc8058516037d284270cClemens Ladisch spin_unlock_irq(&client->lock); 99219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 9936e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter a->handle = 0; 994abaa5743e340c23922d92c9a5a6753ea3ae71e58Kristian Høgsberg 99519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg return 0; 99619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg} 99719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 998872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richterstatic int ioctl_set_iso_channels(struct client *client, union ioctl_arg *arg) 999872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter{ 1000872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter struct fw_cdev_set_iso_channels *a = &arg->set_iso_channels; 1001872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter struct fw_iso_context *ctx = client->iso_context; 1002872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter 1003872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter if (ctx == NULL || a->handle != 0) 1004872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter return -EINVAL; 1005872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter 1006872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter return fw_iso_context_set_channels(ctx, &a->channels); 1007872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter} 1008872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter 10091ca31ae7cfed3e2a8e48fbf6ed6cac06495b6158Kristian Høgsberg/* Macros for decoding the iso packet control header. */ 10101ca31ae7cfed3e2a8e48fbf6ed6cac06495b6158Kristian Høgsberg#define GET_PAYLOAD_LENGTH(v) ((v) & 0xffff) 10111ca31ae7cfed3e2a8e48fbf6ed6cac06495b6158Kristian Høgsberg#define GET_INTERRUPT(v) (((v) >> 16) & 0x01) 10121ca31ae7cfed3e2a8e48fbf6ed6cac06495b6158Kristian Høgsberg#define GET_SKIP(v) (((v) >> 17) & 0x01) 10137a1003449c693f0d57443c8786bbf19717921ae0Stefan Richter#define GET_TAG(v) (((v) >> 18) & 0x03) 10147a1003449c693f0d57443c8786bbf19717921ae0Stefan Richter#define GET_SY(v) (((v) >> 20) & 0x0f) 10151ca31ae7cfed3e2a8e48fbf6ed6cac06495b6158Kristian Høgsberg#define GET_HEADER_LENGTH(v) (((v) >> 24) & 0xff) 10161ca31ae7cfed3e2a8e48fbf6ed6cac06495b6158Kristian Høgsberg 10176e95dea728f4af36c033fcf2318529bd46dae540Stefan Richterstatic int ioctl_queue_iso(struct client *client, union ioctl_arg *arg) 101819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{ 10196e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter struct fw_cdev_queue_iso *a = &arg->queue_iso; 102019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg struct fw_cdev_iso_packet __user *p, *end, *next; 10219b32d5f3074e9b1afaa39a360a59fd77a2214783Kristian Høgsberg struct fw_iso_context *ctx = client->iso_context; 1022872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter unsigned long payload, buffer_end, transmit_header_bytes = 0; 10231ca31ae7cfed3e2a8e48fbf6ed6cac06495b6158Kristian Høgsberg u32 control; 102419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg int count; 102519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg struct { 102619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg struct fw_iso_packet packet; 102719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg u8 header[256]; 102819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg } u; 102919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 10306e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter if (ctx == NULL || a->handle != 0) 103119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg return -EINVAL; 103219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 1033c781c06d119d04601727f2fbc30151e6760d536dKristian Høgsberg /* 1034c781c06d119d04601727f2fbc30151e6760d536dKristian Høgsberg * If the user passes a non-NULL data pointer, has mmap()'ed 103519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg * the iso buffer, and the pointer points inside the buffer, 103619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg * we setup the payload pointers accordingly. Otherwise we 10379aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg * set them both to 0, which will still let packets with 103819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg * payload_length == 0 through. In other words, if no packets 103919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg * use the indirect payload, the iso buffer need not be mapped 10406e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter * and the a->data pointer is ignored. 1041c781c06d119d04601727f2fbc30151e6760d536dKristian Høgsberg */ 10426e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter payload = (unsigned long)a->data - client->vm_start; 1043ef370ee74b7a9cb769d50bfb73b4023ee3e37719Kristian Høgsberg buffer_end = client->buffer.page_count << PAGE_SHIFT; 10446e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter if (a->data == 0 || client->buffer.pages == NULL || 1045ef370ee74b7a9cb769d50bfb73b4023ee3e37719Kristian Høgsberg payload >= buffer_end) { 10469aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg payload = 0; 1047ef370ee74b7a9cb769d50bfb73b4023ee3e37719Kristian Høgsberg buffer_end = 0; 104819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg } 104919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 1050872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter if (ctx->type == FW_ISO_CONTEXT_RECEIVE_MULTICHANNEL && payload & 3) 1051872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter return -EINVAL; 10521ccc9147f6a063c42fef67acff34de18435a4a6bAl Viro 1053872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter p = (struct fw_cdev_iso_packet __user *)u64_to_uptr(a->packets); 10546e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter if (!access_ok(VERIFY_READ, p, a->size)) 105519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg return -EFAULT; 105619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 10576e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter end = (void __user *)p + a->size; 105819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg count = 0; 105919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg while (p < end) { 10601ca31ae7cfed3e2a8e48fbf6ed6cac06495b6158Kristian Høgsberg if (get_user(control, &p->control)) 106119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg return -EFAULT; 10621ca31ae7cfed3e2a8e48fbf6ed6cac06495b6158Kristian Høgsberg u.packet.payload_length = GET_PAYLOAD_LENGTH(control); 10631ca31ae7cfed3e2a8e48fbf6ed6cac06495b6158Kristian Høgsberg u.packet.interrupt = GET_INTERRUPT(control); 10641ca31ae7cfed3e2a8e48fbf6ed6cac06495b6158Kristian Høgsberg u.packet.skip = GET_SKIP(control); 10651ca31ae7cfed3e2a8e48fbf6ed6cac06495b6158Kristian Høgsberg u.packet.tag = GET_TAG(control); 10661ca31ae7cfed3e2a8e48fbf6ed6cac06495b6158Kristian Høgsberg u.packet.sy = GET_SY(control); 10671ca31ae7cfed3e2a8e48fbf6ed6cac06495b6158Kristian Høgsberg u.packet.header_length = GET_HEADER_LENGTH(control); 1068295e3feb92e5073ec32a3c626302d4b92c4c8a95Kristian Høgsberg 1069872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter switch (ctx->type) { 1070872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter case FW_ISO_CONTEXT_TRANSMIT: 1071872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter if (u.packet.header_length & 3) 1072385ab5bcd4be586dffdba550b310308d89eade71Clemens Ladisch return -EINVAL; 1073ae2a97661482c1d0f1aa41b837da95054d0e9a1bStefan Richter transmit_header_bytes = u.packet.header_length; 1074872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter break; 1075872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter 1076872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter case FW_ISO_CONTEXT_RECEIVE: 107769e61d0c07fa28a05f699723a88d49e0014019b6Stefan Richter if (u.packet.header_length == 0 || 107869e61d0c07fa28a05f699723a88d49e0014019b6Stefan Richter u.packet.header_length % ctx->header_size != 0) 1079385ab5bcd4be586dffdba550b310308d89eade71Clemens Ladisch return -EINVAL; 1080872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter break; 1081872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter 1082872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter case FW_ISO_CONTEXT_RECEIVE_MULTICHANNEL: 1083872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter if (u.packet.payload_length == 0 || 1084872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter u.packet.payload_length & 3) 1085295e3feb92e5073ec32a3c626302d4b92c4c8a95Kristian Høgsberg return -EINVAL; 1086872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter break; 1087295e3feb92e5073ec32a3c626302d4b92c4c8a95Kristian Høgsberg } 1088295e3feb92e5073ec32a3c626302d4b92c4c8a95Kristian Høgsberg 108919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg next = (struct fw_cdev_iso_packet __user *) 1090ae2a97661482c1d0f1aa41b837da95054d0e9a1bStefan Richter &p->header[transmit_header_bytes / 4]; 109119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg if (next > end) 109219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg return -EINVAL; 109319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg if (__copy_from_user 1094ae2a97661482c1d0f1aa41b837da95054d0e9a1bStefan Richter (u.packet.header, p->header, transmit_header_bytes)) 109519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg return -EFAULT; 109698b6cbe83b6e8db54638746c9040c7962d96b322Kristian Høgsberg if (u.packet.skip && ctx->type == FW_ISO_CONTEXT_TRANSMIT && 109719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg u.packet.header_length + u.packet.payload_length > 0) 109819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg return -EINVAL; 1099ef370ee74b7a9cb769d50bfb73b4023ee3e37719Kristian Høgsberg if (payload + u.packet.payload_length > buffer_end) 110019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg return -EINVAL; 110119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 11029b32d5f3074e9b1afaa39a360a59fd77a2214783Kristian Høgsberg if (fw_iso_context_queue(ctx, &u.packet, 11039b32d5f3074e9b1afaa39a360a59fd77a2214783Kristian Høgsberg &client->buffer, payload)) 110419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg break; 110519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 110619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg p = next; 110719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg payload += u.packet.payload_length; 110819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg count++; 110919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg } 111013882a82ee1646336c3996c93b4a560a55d2a419Clemens Ladisch fw_iso_context_queue_flush(ctx); 111119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 11126e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter a->size -= uptr_to_u64(p) - a->packets; 11136e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter a->packets = uptr_to_u64(p); 11146e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter a->data = client->vm_start + payload; 111519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 111619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg return count; 111719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg} 111819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 11196e95dea728f4af36c033fcf2318529bd46dae540Stefan Richterstatic int ioctl_start_iso(struct client *client, union ioctl_arg *arg) 112019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{ 11216e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter struct fw_cdev_start_iso *a = &arg->start_iso; 112219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 1123eb5b35a560510efc6bb62f05c3c82e9596cdfafeStefan Richter BUILD_BUG_ON( 1124eb5b35a560510efc6bb62f05c3c82e9596cdfafeStefan Richter FW_CDEV_ISO_CONTEXT_MATCH_TAG0 != FW_ISO_CONTEXT_MATCH_TAG0 || 1125eb5b35a560510efc6bb62f05c3c82e9596cdfafeStefan Richter FW_CDEV_ISO_CONTEXT_MATCH_TAG1 != FW_ISO_CONTEXT_MATCH_TAG1 || 1126eb5b35a560510efc6bb62f05c3c82e9596cdfafeStefan Richter FW_CDEV_ISO_CONTEXT_MATCH_TAG2 != FW_ISO_CONTEXT_MATCH_TAG2 || 1127eb5b35a560510efc6bb62f05c3c82e9596cdfafeStefan Richter FW_CDEV_ISO_CONTEXT_MATCH_TAG3 != FW_ISO_CONTEXT_MATCH_TAG3 || 1128eb5b35a560510efc6bb62f05c3c82e9596cdfafeStefan Richter FW_CDEV_ISO_CONTEXT_MATCH_ALL_TAGS != FW_ISO_CONTEXT_MATCH_ALL_TAGS); 1129eb5b35a560510efc6bb62f05c3c82e9596cdfafeStefan Richter 11306e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter if (client->iso_context == NULL || a->handle != 0) 1131abaa5743e340c23922d92c9a5a6753ea3ae71e58Kristian Høgsberg return -EINVAL; 1132fae603121428ba83b7343c88e68a7144525ab3ebStefan Richter 11336e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter if (client->iso_context->type == FW_ISO_CONTEXT_RECEIVE && 11346e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter (a->tags == 0 || a->tags > 15 || a->sync > 15)) 11356e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter return -EINVAL; 1136eb0306eac0aad0b7da18d8fbfb777f155b2c010dKristian Høgsberg 11376e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter return fw_iso_context_start(client->iso_context, 11386e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter a->cycle, a->sync, a->tags); 113919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg} 114019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 11416e95dea728f4af36c033fcf2318529bd46dae540Stefan Richterstatic int ioctl_stop_iso(struct client *client, union ioctl_arg *arg) 1142b82956685aab4a9d333714300eb8a86fed6c9ab3Kristian Høgsberg{ 11436e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter struct fw_cdev_stop_iso *a = &arg->stop_iso; 1144abaa5743e340c23922d92c9a5a6753ea3ae71e58Kristian Høgsberg 11456e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter if (client->iso_context == NULL || a->handle != 0) 1146abaa5743e340c23922d92c9a5a6753ea3ae71e58Kristian Høgsberg return -EINVAL; 1147abaa5743e340c23922d92c9a5a6753ea3ae71e58Kristian Høgsberg 1148b82956685aab4a9d333714300eb8a86fed6c9ab3Kristian Høgsberg return fw_iso_context_stop(client->iso_context); 1149b82956685aab4a9d333714300eb8a86fed6c9ab3Kristian Høgsberg} 1150b82956685aab4a9d333714300eb8a86fed6c9ab3Kristian Høgsberg 11516e95dea728f4af36c033fcf2318529bd46dae540Stefan Richterstatic int ioctl_get_cycle_timer2(struct client *client, union ioctl_arg *arg) 1152a64408b96b5f67c2778958a230b5cfa3408a4a81Stefan Richter{ 11536e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter struct fw_cdev_get_cycle_timer2 *a = &arg->get_cycle_timer2; 1154a64408b96b5f67c2778958a230b5cfa3408a4a81Stefan Richter struct fw_card *card = client->device->card; 1155abfe5a01ef1e463cbafdae461b693db34e308c02Stefan Richter struct timespec ts = {0, 0}; 11564a9bde9b8ab55a2bb51b57cad215a97bcf80bae2Stefan Richter u32 cycle_time; 1157abfe5a01ef1e463cbafdae461b693db34e308c02Stefan Richter int ret = 0; 1158a64408b96b5f67c2778958a230b5cfa3408a4a81Stefan Richter 11594a9bde9b8ab55a2bb51b57cad215a97bcf80bae2Stefan Richter local_irq_disable(); 1160a64408b96b5f67c2778958a230b5cfa3408a4a81Stefan Richter 11610fcff4e39323f466a47684d7c8ffa77e1be86c8aStefan Richter cycle_time = card->driver->read_csr(card, CSR_CYCLE_TIME); 1162abfe5a01ef1e463cbafdae461b693db34e308c02Stefan Richter 11636e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter switch (a->clk_id) { 1164abfe5a01ef1e463cbafdae461b693db34e308c02Stefan Richter case CLOCK_REALTIME: getnstimeofday(&ts); break; 1165abfe5a01ef1e463cbafdae461b693db34e308c02Stefan Richter case CLOCK_MONOTONIC: do_posix_clock_monotonic_gettime(&ts); break; 1166abfe5a01ef1e463cbafdae461b693db34e308c02Stefan Richter case CLOCK_MONOTONIC_RAW: getrawmonotonic(&ts); break; 1167abfe5a01ef1e463cbafdae461b693db34e308c02Stefan Richter default: 1168abfe5a01ef1e463cbafdae461b693db34e308c02Stefan Richter ret = -EINVAL; 1169abfe5a01ef1e463cbafdae461b693db34e308c02Stefan Richter } 1170a64408b96b5f67c2778958a230b5cfa3408a4a81Stefan Richter 11714a9bde9b8ab55a2bb51b57cad215a97bcf80bae2Stefan Richter local_irq_enable(); 1172a64408b96b5f67c2778958a230b5cfa3408a4a81Stefan Richter 11736e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter a->tv_sec = ts.tv_sec; 11746e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter a->tv_nsec = ts.tv_nsec; 11756e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter a->cycle_timer = cycle_time; 1176abfe5a01ef1e463cbafdae461b693db34e308c02Stefan Richter 1177abfe5a01ef1e463cbafdae461b693db34e308c02Stefan Richter return ret; 1178abfe5a01ef1e463cbafdae461b693db34e308c02Stefan Richter} 1179abfe5a01ef1e463cbafdae461b693db34e308c02Stefan Richter 11806e95dea728f4af36c033fcf2318529bd46dae540Stefan Richterstatic int ioctl_get_cycle_timer(struct client *client, union ioctl_arg *arg) 1181abfe5a01ef1e463cbafdae461b693db34e308c02Stefan Richter{ 11826e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter struct fw_cdev_get_cycle_timer *a = &arg->get_cycle_timer; 1183abfe5a01ef1e463cbafdae461b693db34e308c02Stefan Richter struct fw_cdev_get_cycle_timer2 ct2; 1184abfe5a01ef1e463cbafdae461b693db34e308c02Stefan Richter 1185abfe5a01ef1e463cbafdae461b693db34e308c02Stefan Richter ct2.clk_id = CLOCK_REALTIME; 11866e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter ioctl_get_cycle_timer2(client, (union ioctl_arg *)&ct2); 1187abfe5a01ef1e463cbafdae461b693db34e308c02Stefan Richter 11886e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter a->local_time = ct2.tv_sec * USEC_PER_SEC + ct2.tv_nsec / NSEC_PER_USEC; 11896e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter a->cycle_timer = ct2.cycle_timer; 11904a9bde9b8ab55a2bb51b57cad215a97bcf80bae2Stefan Richter 1191a64408b96b5f67c2778958a230b5cfa3408a4a81Stefan Richter return 0; 1192a64408b96b5f67c2778958a230b5cfa3408a4a81Stefan Richter} 1193a64408b96b5f67c2778958a230b5cfa3408a4a81Stefan Richter 1194b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richterstatic void iso_resource_work(struct work_struct *work) 1195b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter{ 1196b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter struct iso_resource_event *e; 1197b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter struct iso_resource *r = 1198b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter container_of(work, struct iso_resource, work.work); 1199b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter struct client *client = r->client; 1200b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter int generation, channel, bandwidth, todo; 1201b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter bool skip, free, success; 1202b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter 1203b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter spin_lock_irq(&client->lock); 1204b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter generation = client->device->generation; 1205b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter todo = r->todo; 1206b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter /* Allow 1000ms grace period for other reallocations. */ 1207b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter if (todo == ISO_RES_ALLOC && 1208e71084af58cf15e6043338500eeaf6281d0a62afClemens Ladisch time_before64(get_jiffies_64(), 1209e71084af58cf15e6043338500eeaf6281d0a62afClemens Ladisch client->device->card->reset_jiffies + HZ)) { 12109fb551bf72929b316abb6d96cfb2ec05e896042aStefan Richter schedule_iso_resource(r, DIV_ROUND_UP(HZ, 3)); 1211b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter skip = true; 1212b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter } else { 1213b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter /* We could be called twice within the same generation. */ 1214b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter skip = todo == ISO_RES_REALLOC && 1215b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter r->generation == generation; 1216b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter } 12171ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter free = todo == ISO_RES_DEALLOC || 12181ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter todo == ISO_RES_ALLOC_ONCE || 12191ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter todo == ISO_RES_DEALLOC_ONCE; 1220b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter r->generation = generation; 1221b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter spin_unlock_irq(&client->lock); 1222b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter 1223b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter if (skip) 1224b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter goto out; 1225b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter 1226b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter bandwidth = r->bandwidth; 1227b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter 1228b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter fw_iso_resource_manage(client->device->card, generation, 1229b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter r->channels, &channel, &bandwidth, 12301ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter todo == ISO_RES_ALLOC || 12311ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter todo == ISO_RES_REALLOC || 1232f30e6d3e419bfb5540fa82ba7eca01d578556e6bStefan Richter todo == ISO_RES_ALLOC_ONCE); 1233b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter /* 1234b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter * Is this generation outdated already? As long as this resource sticks 1235b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter * in the idr, it will be scheduled again for a newer generation or at 1236b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter * shutdown. 1237b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter */ 1238b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter if (channel == -EAGAIN && 1239b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter (todo == ISO_RES_ALLOC || todo == ISO_RES_REALLOC)) 1240b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter goto out; 1241b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter 1242b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter success = channel >= 0 || bandwidth > 0; 1243b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter 1244b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter spin_lock_irq(&client->lock); 1245b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter /* 1246b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter * Transit from allocation to reallocation, except if the client 1247b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter * requested deallocation in the meantime. 1248b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter */ 1249b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter if (r->todo == ISO_RES_ALLOC) 1250b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter r->todo = ISO_RES_REALLOC; 1251b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter /* 1252b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter * Allocation or reallocation failure? Pull this resource out of the 1253b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter * idr and prepare for deletion, unless the client is shutting down. 1254b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter */ 1255b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter if (r->todo == ISO_RES_REALLOC && !success && 1256b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter !client->in_shutdown && 1257b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter idr_find(&client->resource_idr, r->resource.handle)) { 1258b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter idr_remove(&client->resource_idr, r->resource.handle); 1259b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter client_put(client); 1260b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter free = true; 1261b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter } 1262b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter spin_unlock_irq(&client->lock); 1263b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter 1264b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter if (todo == ISO_RES_ALLOC && channel >= 0) 12655d9cb7d276a9c465fef5a771792eac2cf1929f2bStefan Richter r->channels = 1ULL << channel; 1266b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter 1267b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter if (todo == ISO_RES_REALLOC && success) 1268b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter goto out; 1269b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter 12701ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter if (todo == ISO_RES_ALLOC || todo == ISO_RES_ALLOC_ONCE) { 1271b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter e = r->e_alloc; 1272b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter r->e_alloc = NULL; 1273b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter } else { 1274b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter e = r->e_dealloc; 1275b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter r->e_dealloc = NULL; 1276b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter } 1277e21fcf798e246202d7b60e864f1d7302ebaaf41cStefan Richter e->iso_resource.handle = r->resource.handle; 1278e21fcf798e246202d7b60e864f1d7302ebaaf41cStefan Richter e->iso_resource.channel = channel; 1279e21fcf798e246202d7b60e864f1d7302ebaaf41cStefan Richter e->iso_resource.bandwidth = bandwidth; 1280b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter 1281b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter queue_event(client, &e->event, 1282e21fcf798e246202d7b60e864f1d7302ebaaf41cStefan Richter &e->iso_resource, sizeof(e->iso_resource), NULL, 0); 1283b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter 1284b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter if (free) { 1285b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter cancel_delayed_work(&r->work); 1286b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter kfree(r->e_alloc); 1287b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter kfree(r->e_dealloc); 1288b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter kfree(r); 1289b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter } 1290b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter out: 1291b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter client_put(client); 1292b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter} 1293b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter 1294b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richterstatic void release_iso_resource(struct client *client, 1295b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter struct client_resource *resource) 1296b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter{ 1297b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter struct iso_resource *r = 1298b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter container_of(resource, struct iso_resource, resource); 1299b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter 1300b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter spin_lock_irq(&client->lock); 1301b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter r->todo = ISO_RES_DEALLOC; 13029fb551bf72929b316abb6d96cfb2ec05e896042aStefan Richter schedule_iso_resource(r, 0); 1303b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter spin_unlock_irq(&client->lock); 1304b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter} 1305b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter 13061ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richterstatic int init_iso_resource(struct client *client, 13071ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter struct fw_cdev_allocate_iso_resource *request, int todo) 1308b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter{ 1309b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter struct iso_resource_event *e1, *e2; 1310b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter struct iso_resource *r; 1311b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter int ret; 1312b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter 1313b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter if ((request->channels == 0 && request->bandwidth == 0) || 1314b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter request->bandwidth > BANDWIDTH_AVAILABLE_INITIAL || 1315b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter request->bandwidth < 0) 1316b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter return -EINVAL; 1317b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter 1318b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter r = kmalloc(sizeof(*r), GFP_KERNEL); 1319b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter e1 = kmalloc(sizeof(*e1), GFP_KERNEL); 1320b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter e2 = kmalloc(sizeof(*e2), GFP_KERNEL); 1321b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter if (r == NULL || e1 == NULL || e2 == NULL) { 1322b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter ret = -ENOMEM; 1323b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter goto fail; 1324b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter } 1325b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter 1326b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter INIT_DELAYED_WORK(&r->work, iso_resource_work); 1327b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter r->client = client; 13281ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter r->todo = todo; 1329b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter r->generation = -1; 1330b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter r->channels = request->channels; 1331b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter r->bandwidth = request->bandwidth; 1332b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter r->e_alloc = e1; 1333b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter r->e_dealloc = e2; 1334b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter 1335e21fcf798e246202d7b60e864f1d7302ebaaf41cStefan Richter e1->iso_resource.closure = request->closure; 1336e21fcf798e246202d7b60e864f1d7302ebaaf41cStefan Richter e1->iso_resource.type = FW_CDEV_EVENT_ISO_RESOURCE_ALLOCATED; 1337e21fcf798e246202d7b60e864f1d7302ebaaf41cStefan Richter e2->iso_resource.closure = request->closure; 1338e21fcf798e246202d7b60e864f1d7302ebaaf41cStefan Richter e2->iso_resource.type = FW_CDEV_EVENT_ISO_RESOURCE_DEALLOCATED; 1339b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter 13401ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter if (todo == ISO_RES_ALLOC) { 13411ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter r->resource.release = release_iso_resource; 13421ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter ret = add_client_resource(client, &r->resource, GFP_KERNEL); 134381610b8fbfc027a67707ff567d490819a3d55844Stefan Richter if (ret < 0) 134481610b8fbfc027a67707ff567d490819a3d55844Stefan Richter goto fail; 13451ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter } else { 13461ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter r->resource.release = NULL; 13471ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter r->resource.handle = -1; 13489fb551bf72929b316abb6d96cfb2ec05e896042aStefan Richter schedule_iso_resource(r, 0); 13491ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter } 1350b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter request->handle = r->resource.handle; 1351b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter 1352b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter return 0; 1353b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter fail: 1354b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter kfree(r); 1355b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter kfree(e1); 1356b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter kfree(e2); 1357b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter 1358b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter return ret; 1359b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter} 1360b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter 13616e95dea728f4af36c033fcf2318529bd46dae540Stefan Richterstatic int ioctl_allocate_iso_resource(struct client *client, 13626e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter union ioctl_arg *arg) 13631ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter{ 13646e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter return init_iso_resource(client, 13656e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter &arg->allocate_iso_resource, ISO_RES_ALLOC); 13661ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter} 13671ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter 13686e95dea728f4af36c033fcf2318529bd46dae540Stefan Richterstatic int ioctl_deallocate_iso_resource(struct client *client, 13696e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter union ioctl_arg *arg) 1370b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter{ 13716e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter return release_client_resource(client, 13726e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter arg->deallocate.handle, release_iso_resource, NULL); 1373b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter} 1374b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter 13756e95dea728f4af36c033fcf2318529bd46dae540Stefan Richterstatic int ioctl_allocate_iso_resource_once(struct client *client, 13766e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter union ioctl_arg *arg) 13771ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter{ 13786e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter return init_iso_resource(client, 13796e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter &arg->allocate_iso_resource, ISO_RES_ALLOC_ONCE); 13801ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter} 13811ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter 13826e95dea728f4af36c033fcf2318529bd46dae540Stefan Richterstatic int ioctl_deallocate_iso_resource_once(struct client *client, 13836e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter union ioctl_arg *arg) 13841ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter{ 13856e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter return init_iso_resource(client, 13866e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter &arg->allocate_iso_resource, ISO_RES_DEALLOC_ONCE); 13871ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter} 13881ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter 1389c8a25900f35e575938c791507894c036c0f2ca7dStefan Richter/* 1390c8a25900f35e575938c791507894c036c0f2ca7dStefan Richter * Returns a speed code: Maximum speed to or from this device, 1391c8a25900f35e575938c791507894c036c0f2ca7dStefan Richter * limited by the device's link speed, the local node's link speed, 1392c8a25900f35e575938c791507894c036c0f2ca7dStefan Richter * and all PHY port speeds between the two links. 1393c8a25900f35e575938c791507894c036c0f2ca7dStefan Richter */ 13946e95dea728f4af36c033fcf2318529bd46dae540Stefan Richterstatic int ioctl_get_speed(struct client *client, union ioctl_arg *arg) 139533580a3ef5ba3bc0ee1b520df82a24bb37ce28f0Stefan Richter{ 1396c8a25900f35e575938c791507894c036c0f2ca7dStefan Richter return client->device->max_speed; 139733580a3ef5ba3bc0ee1b520df82a24bb37ce28f0Stefan Richter} 139833580a3ef5ba3bc0ee1b520df82a24bb37ce28f0Stefan Richter 13996e95dea728f4af36c033fcf2318529bd46dae540Stefan Richterstatic int ioctl_send_broadcast_request(struct client *client, 14006e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter union ioctl_arg *arg) 1401acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter{ 14026e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter struct fw_cdev_send_request *a = &arg->send_request; 1403acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter 14046e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter switch (a->tcode) { 1405acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter case TCODE_WRITE_QUADLET_REQUEST: 1406acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter case TCODE_WRITE_BLOCK_REQUEST: 1407acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter break; 1408acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter default: 1409acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter return -EINVAL; 1410acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter } 1411acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter 14121566f3dc3e5986a16c7bbb3bb95bb691251a8d25Stefan Richter /* Security policy: Only allow accesses to Units Space. */ 14136e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter if (a->offset < CSR_REGISTER_BASE + CSR_CONFIG_ROM_END) 14141566f3dc3e5986a16c7bbb3bb95bb691251a8d25Stefan Richter return -EACCES; 14151566f3dc3e5986a16c7bbb3bb95bb691251a8d25Stefan Richter 14166e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter return init_request(client, a, LOCAL_BUS | 0x3f, SCODE_100); 1417acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter} 1418acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter 14196e95dea728f4af36c033fcf2318529bd46dae540Stefan Richterstatic int ioctl_send_stream_packet(struct client *client, union ioctl_arg *arg) 1420f8c2287c65f8f72000102fc058232669e4540bc4Jay Fenlason{ 14216e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter struct fw_cdev_send_stream_packet *a = &arg->send_stream_packet; 142218e9b10fcdc090d3a38606958167d5923c7099b7Stefan Richter struct fw_cdev_send_request request; 142318e9b10fcdc090d3a38606958167d5923c7099b7Stefan Richter int dest; 1424f8c2287c65f8f72000102fc058232669e4540bc4Jay Fenlason 14256e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter if (a->speed > client->device->card->link_speed || 14266e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter a->length > 1024 << a->speed) 142718e9b10fcdc090d3a38606958167d5923c7099b7Stefan Richter return -EIO; 1428f8c2287c65f8f72000102fc058232669e4540bc4Jay Fenlason 14296e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter if (a->tag > 3 || a->channel > 63 || a->sy > 15) 143018e9b10fcdc090d3a38606958167d5923c7099b7Stefan Richter return -EINVAL; 143118e9b10fcdc090d3a38606958167d5923c7099b7Stefan Richter 14326e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter dest = fw_stream_packet_destination_id(a->tag, a->channel, a->sy); 143318e9b10fcdc090d3a38606958167d5923c7099b7Stefan Richter request.tcode = TCODE_STREAM_DATA; 14346e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter request.length = a->length; 14356e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter request.closure = a->closure; 14366e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter request.data = a->data; 14376e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter request.generation = a->generation; 143818e9b10fcdc090d3a38606958167d5923c7099b7Stefan Richter 14396e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter return init_request(client, &request, dest, a->speed); 1440f8c2287c65f8f72000102fc058232669e4540bc4Jay Fenlason} 1441f8c2287c65f8f72000102fc058232669e4540bc4Jay Fenlason 1442850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richterstatic void outbound_phy_packet_callback(struct fw_packet *packet, 1443850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter struct fw_card *card, int status) 1444850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter{ 1445850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter struct outbound_phy_packet_event *e = 1446850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter container_of(packet, struct outbound_phy_packet_event, p); 1447850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter 1448850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter switch (status) { 1449850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter /* expected: */ 1450850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter case ACK_COMPLETE: e->phy_packet.rcode = RCODE_COMPLETE; break; 1451850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter /* should never happen with PHY packets: */ 1452850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter case ACK_PENDING: e->phy_packet.rcode = RCODE_COMPLETE; break; 1453850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter case ACK_BUSY_X: 1454850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter case ACK_BUSY_A: 1455850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter case ACK_BUSY_B: e->phy_packet.rcode = RCODE_BUSY; break; 1456850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter case ACK_DATA_ERROR: e->phy_packet.rcode = RCODE_DATA_ERROR; break; 1457850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter case ACK_TYPE_ERROR: e->phy_packet.rcode = RCODE_TYPE_ERROR; break; 1458850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter /* stale generation; cancelled; on certain controllers: no ack */ 1459850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter default: e->phy_packet.rcode = status; break; 1460850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter } 1461cc550216ae9a2993ef3973464714dc1a39ab1f86Stefan Richter e->phy_packet.data[0] = packet->timestamp; 1462850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter 1463cc550216ae9a2993ef3973464714dc1a39ab1f86Stefan Richter queue_event(e->client, &e->event, &e->phy_packet, 1464cc550216ae9a2993ef3973464714dc1a39ab1f86Stefan Richter sizeof(e->phy_packet) + e->phy_packet.length, NULL, 0); 1465850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter client_put(e->client); 1466850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter} 1467850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter 1468850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richterstatic int ioctl_send_phy_packet(struct client *client, union ioctl_arg *arg) 1469850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter{ 1470850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter struct fw_cdev_send_phy_packet *a = &arg->send_phy_packet; 1471850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter struct fw_card *card = client->device->card; 1472850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter struct outbound_phy_packet_event *e; 1473850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter 1474850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter /* Access policy: Allow this ioctl only on local nodes' device files. */ 1475850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter if (!client->device->is_local) 1476850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter return -ENOSYS; 1477850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter 1478cc550216ae9a2993ef3973464714dc1a39ab1f86Stefan Richter e = kzalloc(sizeof(*e) + 4, GFP_KERNEL); 1479850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter if (e == NULL) 1480850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter return -ENOMEM; 1481850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter 1482850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter client_get(client); 1483850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter e->client = client; 1484850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter e->p.speed = SCODE_100; 1485850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter e->p.generation = a->generation; 14865b06db166c4d38638980283505259fa165d4f369Clemens Ladisch e->p.header[0] = TCODE_LINK_INTERNAL << 4; 14875b06db166c4d38638980283505259fa165d4f369Clemens Ladisch e->p.header[1] = a->data[0]; 14885b06db166c4d38638980283505259fa165d4f369Clemens Ladisch e->p.header[2] = a->data[1]; 14895b06db166c4d38638980283505259fa165d4f369Clemens Ladisch e->p.header_length = 12; 1490850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter e->p.callback = outbound_phy_packet_callback; 1491850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter e->phy_packet.closure = a->closure; 1492850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter e->phy_packet.type = FW_CDEV_EVENT_PHY_PACKET_SENT; 1493cc550216ae9a2993ef3973464714dc1a39ab1f86Stefan Richter if (is_ping_packet(a->data)) 1494cc550216ae9a2993ef3973464714dc1a39ab1f86Stefan Richter e->phy_packet.length = 4; 1495850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter 1496850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter card->driver->send_request(card, &e->p); 1497850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter 1498850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter return 0; 1499850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter} 1500850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter 1501bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richterstatic int ioctl_receive_phy_packets(struct client *client, union ioctl_arg *arg) 1502bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter{ 1503bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter struct fw_cdev_receive_phy_packets *a = &arg->receive_phy_packets; 1504bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter struct fw_card *card = client->device->card; 1505bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter 1506bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter /* Access policy: Allow this ioctl only on local nodes' device files. */ 1507bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter if (!client->device->is_local) 1508bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter return -ENOSYS; 1509bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter 1510bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter spin_lock_irq(&card->lock); 1511bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter 1512bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter list_move_tail(&client->phy_receiver_link, &card->phy_receiver_list); 1513bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter client->phy_receiver_closure = a->closure; 1514bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter 1515bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter spin_unlock_irq(&card->lock); 1516bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter 1517bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter return 0; 1518bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter} 1519bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter 1520bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richtervoid fw_cdev_handle_phy_packet(struct fw_card *card, struct fw_packet *p) 1521bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter{ 1522bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter struct client *client; 1523bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter struct inbound_phy_packet_event *e; 1524bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter unsigned long flags; 1525bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter 1526bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter spin_lock_irqsave(&card->lock, flags); 1527bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter 1528bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter list_for_each_entry(client, &card->phy_receiver_list, phy_receiver_link) { 1529bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter e = kmalloc(sizeof(*e) + 8, GFP_ATOMIC); 1530bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter if (e == NULL) { 1531bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter fw_notify("Out of memory when allocating event\n"); 1532bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter break; 1533bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter } 1534bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter e->phy_packet.closure = client->phy_receiver_closure; 1535bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter e->phy_packet.type = FW_CDEV_EVENT_PHY_PACKET_RECEIVED; 1536bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter e->phy_packet.rcode = RCODE_COMPLETE; 1537bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter e->phy_packet.length = 8; 1538bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter e->phy_packet.data[0] = p->header[1]; 1539bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter e->phy_packet.data[1] = p->header[2]; 1540bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter queue_event(client, &e->event, 1541bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter &e->phy_packet, sizeof(e->phy_packet) + 8, NULL, 0); 1542bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter } 1543bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter 1544bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter spin_unlock_irqrestore(&card->lock, flags); 1545bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter} 1546bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter 15476e95dea728f4af36c033fcf2318529bd46dae540Stefan Richterstatic int (* const ioctl_handlers[])(struct client *, union ioctl_arg *) = { 1548b9dc61cf404165fb77e80c853e9fec9af258f9ceStefan Richter [0x00] = ioctl_get_info, 1549b9dc61cf404165fb77e80c853e9fec9af258f9ceStefan Richter [0x01] = ioctl_send_request, 1550b9dc61cf404165fb77e80c853e9fec9af258f9ceStefan Richter [0x02] = ioctl_allocate, 1551b9dc61cf404165fb77e80c853e9fec9af258f9ceStefan Richter [0x03] = ioctl_deallocate, 1552b9dc61cf404165fb77e80c853e9fec9af258f9ceStefan Richter [0x04] = ioctl_send_response, 1553b9dc61cf404165fb77e80c853e9fec9af258f9ceStefan Richter [0x05] = ioctl_initiate_bus_reset, 1554b9dc61cf404165fb77e80c853e9fec9af258f9ceStefan Richter [0x06] = ioctl_add_descriptor, 1555b9dc61cf404165fb77e80c853e9fec9af258f9ceStefan Richter [0x07] = ioctl_remove_descriptor, 1556b9dc61cf404165fb77e80c853e9fec9af258f9ceStefan Richter [0x08] = ioctl_create_iso_context, 1557b9dc61cf404165fb77e80c853e9fec9af258f9ceStefan Richter [0x09] = ioctl_queue_iso, 1558b9dc61cf404165fb77e80c853e9fec9af258f9ceStefan Richter [0x0a] = ioctl_start_iso, 1559b9dc61cf404165fb77e80c853e9fec9af258f9ceStefan Richter [0x0b] = ioctl_stop_iso, 1560b9dc61cf404165fb77e80c853e9fec9af258f9ceStefan Richter [0x0c] = ioctl_get_cycle_timer, 1561b9dc61cf404165fb77e80c853e9fec9af258f9ceStefan Richter [0x0d] = ioctl_allocate_iso_resource, 1562b9dc61cf404165fb77e80c853e9fec9af258f9ceStefan Richter [0x0e] = ioctl_deallocate_iso_resource, 1563b9dc61cf404165fb77e80c853e9fec9af258f9ceStefan Richter [0x0f] = ioctl_allocate_iso_resource_once, 1564b9dc61cf404165fb77e80c853e9fec9af258f9ceStefan Richter [0x10] = ioctl_deallocate_iso_resource_once, 1565b9dc61cf404165fb77e80c853e9fec9af258f9ceStefan Richter [0x11] = ioctl_get_speed, 1566b9dc61cf404165fb77e80c853e9fec9af258f9ceStefan Richter [0x12] = ioctl_send_broadcast_request, 1567b9dc61cf404165fb77e80c853e9fec9af258f9ceStefan Richter [0x13] = ioctl_send_stream_packet, 1568b9dc61cf404165fb77e80c853e9fec9af258f9ceStefan Richter [0x14] = ioctl_get_cycle_timer2, 1569850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter [0x15] = ioctl_send_phy_packet, 1570bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter [0x16] = ioctl_receive_phy_packets, 1571872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter [0x17] = ioctl_set_iso_channels, 15724f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg}; 15734f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg 157453dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richterstatic int dispatch_ioctl(struct client *client, 157553dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richter unsigned int cmd, void __user *arg) 157619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{ 15776e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter union ioctl_arg buffer; 15782dbd7d7e2327b0c2cc4e2de903e1cfa19980a504Stefan Richter int ret; 15794f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg 158064582298b9c29535188380f488873e7d2196a2ebStefan Richter if (fw_device_is_shutdown(client->device)) 158164582298b9c29535188380f488873e7d2196a2ebStefan Richter return -ENODEV; 158264582298b9c29535188380f488873e7d2196a2ebStefan Richter 15834f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg if (_IOC_TYPE(cmd) != '#' || 15849cac00b8f0079d5d3d54ec4dae453d58dec30e7cStefan Richter _IOC_NR(cmd) >= ARRAY_SIZE(ioctl_handlers) || 15859cac00b8f0079d5d3d54ec4dae453d58dec30e7cStefan Richter _IOC_SIZE(cmd) > sizeof(buffer)) 158619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg return -EINVAL; 15874f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg 15889cac00b8f0079d5d3d54ec4dae453d58dec30e7cStefan Richter if (_IOC_DIR(cmd) == _IOC_READ) 15899cac00b8f0079d5d3d54ec4dae453d58dec30e7cStefan Richter memset(&buffer, 0, _IOC_SIZE(cmd)); 15909cac00b8f0079d5d3d54ec4dae453d58dec30e7cStefan Richter 15919cac00b8f0079d5d3d54ec4dae453d58dec30e7cStefan Richter if (_IOC_DIR(cmd) & _IOC_WRITE) 15929cac00b8f0079d5d3d54ec4dae453d58dec30e7cStefan Richter if (copy_from_user(&buffer, arg, _IOC_SIZE(cmd))) 15934f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg return -EFAULT; 15944f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg 15956e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter ret = ioctl_handlers[_IOC_NR(cmd)](client, &buffer); 15962dbd7d7e2327b0c2cc4e2de903e1cfa19980a504Stefan Richter if (ret < 0) 15972dbd7d7e2327b0c2cc4e2de903e1cfa19980a504Stefan Richter return ret; 15984f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg 15999cac00b8f0079d5d3d54ec4dae453d58dec30e7cStefan Richter if (_IOC_DIR(cmd) & _IOC_READ) 16009cac00b8f0079d5d3d54ec4dae453d58dec30e7cStefan Richter if (copy_to_user(arg, &buffer, _IOC_SIZE(cmd))) 16014f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg return -EFAULT; 16024f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg 16032dbd7d7e2327b0c2cc4e2de903e1cfa19980a504Stefan Richter return ret; 160419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg} 160519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 160653dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richterstatic long fw_device_op_ioctl(struct file *file, 160753dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richter unsigned int cmd, unsigned long arg) 160819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{ 160964582298b9c29535188380f488873e7d2196a2ebStefan Richter return dispatch_ioctl(file->private_data, cmd, (void __user *)arg); 161019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg} 161119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 161219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg#ifdef CONFIG_COMPAT 161353dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richterstatic long fw_device_op_compat_ioctl(struct file *file, 161453dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richter unsigned int cmd, unsigned long arg) 161519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{ 161664582298b9c29535188380f488873e7d2196a2ebStefan Richter return dispatch_ioctl(file->private_data, cmd, compat_ptr(arg)); 161719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg} 161819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg#endif 161919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 162019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsbergstatic int fw_device_op_mmap(struct file *file, struct vm_area_struct *vma) 162119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{ 162219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg struct client *client = file->private_data; 16239aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg enum dma_data_direction direction; 16249aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg unsigned long size; 16252dbd7d7e2327b0c2cc4e2de903e1cfa19980a504Stefan Richter int page_count, ret; 16269aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg 1627551f4cb9de716ffcdaf968c99a450c22ff12e8c3Jay Fenlason if (fw_device_is_shutdown(client->device)) 1628551f4cb9de716ffcdaf968c99a450c22ff12e8c3Jay Fenlason return -ENODEV; 1629551f4cb9de716ffcdaf968c99a450c22ff12e8c3Jay Fenlason 16309aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg /* FIXME: We could support multiple buffers, but we don't. */ 16319aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg if (client->buffer.pages != NULL) 16329aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg return -EBUSY; 16339aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg 16349aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg if (!(vma->vm_flags & VM_SHARED)) 16359aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg return -EINVAL; 163619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 16379aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg if (vma->vm_start & ~PAGE_MASK) 163819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg return -EINVAL; 163919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 164019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg client->vm_start = vma->vm_start; 16419aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg size = vma->vm_end - vma->vm_start; 16429aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg page_count = size >> PAGE_SHIFT; 16439aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg if (size & ~PAGE_MASK) 16449aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg return -EINVAL; 16459aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg 16469aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg if (vma->vm_flags & VM_WRITE) 16479aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg direction = DMA_TO_DEVICE; 16489aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg else 16499aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg direction = DMA_FROM_DEVICE; 16509aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg 16512dbd7d7e2327b0c2cc4e2de903e1cfa19980a504Stefan Richter ret = fw_iso_buffer_init(&client->buffer, client->device->card, 16522dbd7d7e2327b0c2cc4e2de903e1cfa19980a504Stefan Richter page_count, direction); 16532dbd7d7e2327b0c2cc4e2de903e1cfa19980a504Stefan Richter if (ret < 0) 16542dbd7d7e2327b0c2cc4e2de903e1cfa19980a504Stefan Richter return ret; 165519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 16562dbd7d7e2327b0c2cc4e2de903e1cfa19980a504Stefan Richter ret = fw_iso_buffer_map(&client->buffer, vma); 16572dbd7d7e2327b0c2cc4e2de903e1cfa19980a504Stefan Richter if (ret < 0) 16589aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg fw_iso_buffer_destroy(&client->buffer, client->device->card); 16599aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg 16602dbd7d7e2327b0c2cc4e2de903e1cfa19980a504Stefan Richter return ret; 166119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg} 166219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 16635a5e62da9be255439e8ce59f96828775b7b33374Clemens Ladischstatic int is_outbound_transaction_resource(int id, void *p, void *data) 16645a5e62da9be255439e8ce59f96828775b7b33374Clemens Ladisch{ 16655a5e62da9be255439e8ce59f96828775b7b33374Clemens Ladisch struct client_resource *resource = p; 16665a5e62da9be255439e8ce59f96828775b7b33374Clemens Ladisch 16675a5e62da9be255439e8ce59f96828775b7b33374Clemens Ladisch return resource->release == release_transaction; 16685a5e62da9be255439e8ce59f96828775b7b33374Clemens Ladisch} 16695a5e62da9be255439e8ce59f96828775b7b33374Clemens Ladisch 16705a5e62da9be255439e8ce59f96828775b7b33374Clemens Ladischstatic int has_outbound_transactions(struct client *client) 16715a5e62da9be255439e8ce59f96828775b7b33374Clemens Ladisch{ 16725a5e62da9be255439e8ce59f96828775b7b33374Clemens Ladisch int ret; 16735a5e62da9be255439e8ce59f96828775b7b33374Clemens Ladisch 16745a5e62da9be255439e8ce59f96828775b7b33374Clemens Ladisch spin_lock_irq(&client->lock); 16755a5e62da9be255439e8ce59f96828775b7b33374Clemens Ladisch ret = idr_for_each(&client->resource_idr, 16765a5e62da9be255439e8ce59f96828775b7b33374Clemens Ladisch is_outbound_transaction_resource, NULL); 16775a5e62da9be255439e8ce59f96828775b7b33374Clemens Ladisch spin_unlock_irq(&client->lock); 16785a5e62da9be255439e8ce59f96828775b7b33374Clemens Ladisch 16795a5e62da9be255439e8ce59f96828775b7b33374Clemens Ladisch return ret; 16805a5e62da9be255439e8ce59f96828775b7b33374Clemens Ladisch} 16815a5e62da9be255439e8ce59f96828775b7b33374Clemens Ladisch 168245ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlasonstatic int shutdown_resource(int id, void *p, void *data) 168345ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason{ 1684e21fcf798e246202d7b60e864f1d7302ebaaf41cStefan Richter struct client_resource *resource = p; 168545ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason struct client *client = data; 168645ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason 1687e21fcf798e246202d7b60e864f1d7302ebaaf41cStefan Richter resource->release(client, resource); 1688fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter client_put(client); 168945ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason 169045ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason return 0; 169145ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason} 169245ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason 169319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsbergstatic int fw_device_op_release(struct inode *inode, struct file *file) 169419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{ 169519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg struct client *client = file->private_data; 1696e21fcf798e246202d7b60e864f1d7302ebaaf41cStefan Richter struct event *event, *next_event; 169719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 1698bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter spin_lock_irq(&client->device->card->lock); 1699bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter list_del(&client->phy_receiver_link); 1700bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter spin_unlock_irq(&client->device->card->lock); 1701bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter 170297811e347310766030a648fdf0e407b2c91a39c1Stefan Richter mutex_lock(&client->device->client_list_mutex); 170397811e347310766030a648fdf0e407b2c91a39c1Stefan Richter list_del(&client->link); 170497811e347310766030a648fdf0e407b2c91a39c1Stefan Richter mutex_unlock(&client->device->client_list_mutex); 170597811e347310766030a648fdf0e407b2c91a39c1Stefan Richter 170619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg if (client->iso_context) 170719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg fw_iso_context_destroy(client->iso_context); 170819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 170936a755cfc398fc50abc74055d4478c1b067dac55Stefan Richter if (client->buffer.pages) 171036a755cfc398fc50abc74055d4478c1b067dac55Stefan Richter fw_iso_buffer_destroy(&client->buffer, client->device->card); 171136a755cfc398fc50abc74055d4478c1b067dac55Stefan Richter 171245ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason /* Freeze client->resource_idr and client->event_list */ 17133ba949868a6dc082b24cba5c3bf3f50de7391433Stefan Richter spin_lock_irq(&client->lock); 171445ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason client->in_shutdown = true; 17153ba949868a6dc082b24cba5c3bf3f50de7391433Stefan Richter spin_unlock_irq(&client->lock); 171666dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg 17175a5e62da9be255439e8ce59f96828775b7b33374Clemens Ladisch wait_event(client->tx_flush_wait, !has_outbound_transactions(client)); 17185a5e62da9be255439e8ce59f96828775b7b33374Clemens Ladisch 171945ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason idr_for_each(&client->resource_idr, shutdown_resource, client); 172045ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason idr_remove_all(&client->resource_idr); 172145ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason idr_destroy(&client->resource_idr); 172228cf6a04c82857d562968dc3a8a89726e6ac3dcbKristian Høgsberg 1723e21fcf798e246202d7b60e864f1d7302ebaaf41cStefan Richter list_for_each_entry_safe(event, next_event, &client->event_list, link) 1724e21fcf798e246202d7b60e864f1d7302ebaaf41cStefan Richter kfree(event); 172519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 1726fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter client_put(client); 172719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 172819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg return 0; 172919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg} 173019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 173119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsbergstatic unsigned int fw_device_op_poll(struct file *file, poll_table * pt) 173219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{ 173319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg struct client *client = file->private_data; 17342603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg unsigned int mask = 0; 173519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 173619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg poll_wait(file, &client->wait, pt); 173719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 17382603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg if (fw_device_is_shutdown(client->device)) 17392603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg mask |= POLLHUP | POLLERR; 174019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg if (!list_empty(&client->event_list)) 17412603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg mask |= POLLIN | POLLRDNORM; 17422603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg 17432603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg return mask; 174419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg} 174519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 174621ebcd1224d05c8673053e1e93ab9ec7ef3e0b84Stefan Richterconst struct file_operations fw_device_ops = { 174719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg .owner = THIS_MODULE, 17483ac26b2ee30005930117fe6a180c139c5f300fafStefan Richter .llseek = no_llseek, 174919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg .open = fw_device_op_open, 175019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg .read = fw_device_op_read, 175119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg .unlocked_ioctl = fw_device_op_ioctl, 175219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg .mmap = fw_device_op_mmap, 17533ac26b2ee30005930117fe6a180c139c5f300fafStefan Richter .release = fw_device_op_release, 17543ac26b2ee30005930117fe6a180c139c5f300fafStefan Richter .poll = fw_device_op_poll, 175519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg#ifdef CONFIG_COMPAT 17565af4e5eab30d481f76b89a2167c873dfad960acbStefan Richter .compat_ioctl = fw_device_op_compat_ioctl, 175719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg#endif 175819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg}; 1759