1*4e69fc22SAndras Domokos /* 2*4e69fc22SAndras Domokos * HSI character device driver, implements the character device 3*4e69fc22SAndras Domokos * interface. 4*4e69fc22SAndras Domokos * 5*4e69fc22SAndras Domokos * Copyright (C) 2010 Nokia Corporation. All rights reserved. 6*4e69fc22SAndras Domokos * 7*4e69fc22SAndras Domokos * Contact: Andras Domokos <andras.domokos@nokia.com> 8*4e69fc22SAndras Domokos * 9*4e69fc22SAndras Domokos * This program is free software; you can redistribute it and/or 10*4e69fc22SAndras Domokos * modify it under the terms of the GNU General Public License 11*4e69fc22SAndras Domokos * version 2 as published by the Free Software Foundation. 12*4e69fc22SAndras Domokos * 13*4e69fc22SAndras Domokos * This program is distributed in the hope that it will be useful, but 14*4e69fc22SAndras Domokos * WITHOUT ANY WARRANTY; without even the implied warranty of 15*4e69fc22SAndras Domokos * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16*4e69fc22SAndras Domokos * General Public License for more details. 17*4e69fc22SAndras Domokos * 18*4e69fc22SAndras Domokos * You should have received a copy of the GNU General Public License 19*4e69fc22SAndras Domokos * along with this program; if not, write to the Free Software 20*4e69fc22SAndras Domokos * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 21*4e69fc22SAndras Domokos * 02110-1301 USA 22*4e69fc22SAndras Domokos */ 23*4e69fc22SAndras Domokos 24*4e69fc22SAndras Domokos #include <linux/errno.h> 25*4e69fc22SAndras Domokos #include <linux/types.h> 26*4e69fc22SAndras Domokos #include <linux/atomic.h> 27*4e69fc22SAndras Domokos #include <linux/kernel.h> 28*4e69fc22SAndras Domokos #include <linux/init.h> 29*4e69fc22SAndras Domokos #include <linux/module.h> 30*4e69fc22SAndras Domokos #include <linux/mutex.h> 31*4e69fc22SAndras Domokos #include <linux/list.h> 32*4e69fc22SAndras Domokos #include <linux/slab.h> 33*4e69fc22SAndras Domokos #include <linux/kmemleak.h> 34*4e69fc22SAndras Domokos #include <linux/ioctl.h> 35*4e69fc22SAndras Domokos #include <linux/wait.h> 36*4e69fc22SAndras Domokos #include <linux/fs.h> 37*4e69fc22SAndras Domokos #include <linux/sched.h> 38*4e69fc22SAndras Domokos #include <linux/device.h> 39*4e69fc22SAndras Domokos #include <linux/cdev.h> 40*4e69fc22SAndras Domokos #include <linux/uaccess.h> 41*4e69fc22SAndras Domokos #include <linux/scatterlist.h> 42*4e69fc22SAndras Domokos #include <linux/stat.h> 43*4e69fc22SAndras Domokos #include <linux/hsi/hsi.h> 44*4e69fc22SAndras Domokos #include <linux/hsi/hsi_char.h> 45*4e69fc22SAndras Domokos 46*4e69fc22SAndras Domokos #define HSC_DEVS 16 /* Num of channels */ 47*4e69fc22SAndras Domokos #define HSC_MSGS 4 48*4e69fc22SAndras Domokos 49*4e69fc22SAndras Domokos #define HSC_RXBREAK 0 50*4e69fc22SAndras Domokos 51*4e69fc22SAndras Domokos #define HSC_ID_BITS 6 52*4e69fc22SAndras Domokos #define HSC_PORT_ID_BITS 4 53*4e69fc22SAndras Domokos #define HSC_ID_MASK 3 54*4e69fc22SAndras Domokos #define HSC_PORT_ID_MASK 3 55*4e69fc22SAndras Domokos #define HSC_CH_MASK 0xf 56*4e69fc22SAndras Domokos 57*4e69fc22SAndras Domokos /* 58*4e69fc22SAndras Domokos * We support up to 4 controllers that can have up to 4 59*4e69fc22SAndras Domokos * ports, which should currently be more than enough. 60*4e69fc22SAndras Domokos */ 61*4e69fc22SAndras Domokos #define HSC_BASEMINOR(id, port_id) \ 62*4e69fc22SAndras Domokos ((((id) & HSC_ID_MASK) << HSC_ID_BITS) | \ 63*4e69fc22SAndras Domokos (((port_id) & HSC_PORT_ID_MASK) << HSC_PORT_ID_BITS)) 64*4e69fc22SAndras Domokos 65*4e69fc22SAndras Domokos enum { 66*4e69fc22SAndras Domokos HSC_CH_OPEN, 67*4e69fc22SAndras Domokos HSC_CH_READ, 68*4e69fc22SAndras Domokos HSC_CH_WRITE, 69*4e69fc22SAndras Domokos HSC_CH_WLINE, 70*4e69fc22SAndras Domokos }; 71*4e69fc22SAndras Domokos 72*4e69fc22SAndras Domokos enum { 73*4e69fc22SAndras Domokos HSC_RX, 74*4e69fc22SAndras Domokos HSC_TX, 75*4e69fc22SAndras Domokos }; 76*4e69fc22SAndras Domokos 77*4e69fc22SAndras Domokos struct hsc_client_data; 78*4e69fc22SAndras Domokos /** 79*4e69fc22SAndras Domokos * struct hsc_channel - hsi_char internal channel data 80*4e69fc22SAndras Domokos * @ch: channel number 81*4e69fc22SAndras Domokos * @flags: Keeps state of the channel (open/close, reading, writing) 82*4e69fc22SAndras Domokos * @free_msgs_list: List of free HSI messages/requests 83*4e69fc22SAndras Domokos * @rx_msgs_queue: List of pending RX requests 84*4e69fc22SAndras Domokos * @tx_msgs_queue: List of pending TX requests 85*4e69fc22SAndras Domokos * @lock: Serialize access to the lists 86*4e69fc22SAndras Domokos * @cl: reference to the associated hsi_client 87*4e69fc22SAndras Domokos * @cl_data: reference to the client data that this channels belongs to 88*4e69fc22SAndras Domokos * @rx_wait: RX requests wait queue 89*4e69fc22SAndras Domokos * @tx_wait: TX requests wait queue 90*4e69fc22SAndras Domokos */ 91*4e69fc22SAndras Domokos struct hsc_channel { 92*4e69fc22SAndras Domokos unsigned int ch; 93*4e69fc22SAndras Domokos unsigned long flags; 94*4e69fc22SAndras Domokos struct list_head free_msgs_list; 95*4e69fc22SAndras Domokos struct list_head rx_msgs_queue; 96*4e69fc22SAndras Domokos struct list_head tx_msgs_queue; 97*4e69fc22SAndras Domokos spinlock_t lock; 98*4e69fc22SAndras Domokos struct hsi_client *cl; 99*4e69fc22SAndras Domokos struct hsc_client_data *cl_data; 100*4e69fc22SAndras Domokos wait_queue_head_t rx_wait; 101*4e69fc22SAndras Domokos wait_queue_head_t tx_wait; 102*4e69fc22SAndras Domokos }; 103*4e69fc22SAndras Domokos 104*4e69fc22SAndras Domokos /** 105*4e69fc22SAndras Domokos * struct hsc_client_data - hsi_char internal client data 106*4e69fc22SAndras Domokos * @cdev: Characther device associated to the hsi_client 107*4e69fc22SAndras Domokos * @lock: Lock to serialize open/close access 108*4e69fc22SAndras Domokos * @flags: Keeps track of port state (rx hwbreak armed) 109*4e69fc22SAndras Domokos * @usecnt: Use count for claiming the HSI port (mutex protected) 110*4e69fc22SAndras Domokos * @cl: Referece to the HSI client 111*4e69fc22SAndras Domokos * @channels: Array of channels accessible by the client 112*4e69fc22SAndras Domokos */ 113*4e69fc22SAndras Domokos struct hsc_client_data { 114*4e69fc22SAndras Domokos struct cdev cdev; 115*4e69fc22SAndras Domokos struct mutex lock; 116*4e69fc22SAndras Domokos unsigned long flags; 117*4e69fc22SAndras Domokos unsigned int usecnt; 118*4e69fc22SAndras Domokos struct hsi_client *cl; 119*4e69fc22SAndras Domokos struct hsc_channel channels[HSC_DEVS]; 120*4e69fc22SAndras Domokos }; 121*4e69fc22SAndras Domokos 122*4e69fc22SAndras Domokos /* Stores the major number dynamically allocated for hsi_char */ 123*4e69fc22SAndras Domokos static unsigned int hsc_major; 124*4e69fc22SAndras Domokos /* Maximum buffer size that hsi_char will accept from userspace */ 125*4e69fc22SAndras Domokos static unsigned int max_data_size = 0x1000; 126*4e69fc22SAndras Domokos module_param(max_data_size, uint, S_IRUSR | S_IWUSR); 127*4e69fc22SAndras Domokos MODULE_PARM_DESC(max_data_size, "max read/write data size [4,8..65536] (^2)"); 128*4e69fc22SAndras Domokos 129*4e69fc22SAndras Domokos static void hsc_add_tail(struct hsc_channel *channel, struct hsi_msg *msg, 130*4e69fc22SAndras Domokos struct list_head *queue) 131*4e69fc22SAndras Domokos { 132*4e69fc22SAndras Domokos unsigned long flags; 133*4e69fc22SAndras Domokos 134*4e69fc22SAndras Domokos spin_lock_irqsave(&channel->lock, flags); 135*4e69fc22SAndras Domokos list_add_tail(&msg->link, queue); 136*4e69fc22SAndras Domokos spin_unlock_irqrestore(&channel->lock, flags); 137*4e69fc22SAndras Domokos } 138*4e69fc22SAndras Domokos 139*4e69fc22SAndras Domokos static struct hsi_msg *hsc_get_first_msg(struct hsc_channel *channel, 140*4e69fc22SAndras Domokos struct list_head *queue) 141*4e69fc22SAndras Domokos { 142*4e69fc22SAndras Domokos struct hsi_msg *msg = NULL; 143*4e69fc22SAndras Domokos unsigned long flags; 144*4e69fc22SAndras Domokos 145*4e69fc22SAndras Domokos spin_lock_irqsave(&channel->lock, flags); 146*4e69fc22SAndras Domokos 147*4e69fc22SAndras Domokos if (list_empty(queue)) 148*4e69fc22SAndras Domokos goto out; 149*4e69fc22SAndras Domokos 150*4e69fc22SAndras Domokos msg = list_first_entry(queue, struct hsi_msg, link); 151*4e69fc22SAndras Domokos list_del(&msg->link); 152*4e69fc22SAndras Domokos out: 153*4e69fc22SAndras Domokos spin_unlock_irqrestore(&channel->lock, flags); 154*4e69fc22SAndras Domokos 155*4e69fc22SAndras Domokos return msg; 156*4e69fc22SAndras Domokos } 157*4e69fc22SAndras Domokos 158*4e69fc22SAndras Domokos static inline void hsc_msg_free(struct hsi_msg *msg) 159*4e69fc22SAndras Domokos { 160*4e69fc22SAndras Domokos kfree(sg_virt(msg->sgt.sgl)); 161*4e69fc22SAndras Domokos hsi_free_msg(msg); 162*4e69fc22SAndras Domokos } 163*4e69fc22SAndras Domokos 164*4e69fc22SAndras Domokos static void hsc_free_list(struct list_head *list) 165*4e69fc22SAndras Domokos { 166*4e69fc22SAndras Domokos struct hsi_msg *msg, *tmp; 167*4e69fc22SAndras Domokos 168*4e69fc22SAndras Domokos list_for_each_entry_safe(msg, tmp, list, link) { 169*4e69fc22SAndras Domokos list_del(&msg->link); 170*4e69fc22SAndras Domokos hsc_msg_free(msg); 171*4e69fc22SAndras Domokos } 172*4e69fc22SAndras Domokos } 173*4e69fc22SAndras Domokos 174*4e69fc22SAndras Domokos static void hsc_reset_list(struct hsc_channel *channel, struct list_head *l) 175*4e69fc22SAndras Domokos { 176*4e69fc22SAndras Domokos unsigned long flags; 177*4e69fc22SAndras Domokos LIST_HEAD(list); 178*4e69fc22SAndras Domokos 179*4e69fc22SAndras Domokos spin_lock_irqsave(&channel->lock, flags); 180*4e69fc22SAndras Domokos list_splice_init(l, &list); 181*4e69fc22SAndras Domokos spin_unlock_irqrestore(&channel->lock, flags); 182*4e69fc22SAndras Domokos 183*4e69fc22SAndras Domokos hsc_free_list(&list); 184*4e69fc22SAndras Domokos } 185*4e69fc22SAndras Domokos 186*4e69fc22SAndras Domokos static inline struct hsi_msg *hsc_msg_alloc(unsigned int alloc_size) 187*4e69fc22SAndras Domokos { 188*4e69fc22SAndras Domokos struct hsi_msg *msg; 189*4e69fc22SAndras Domokos void *buf; 190*4e69fc22SAndras Domokos 191*4e69fc22SAndras Domokos msg = hsi_alloc_msg(1, GFP_KERNEL); 192*4e69fc22SAndras Domokos if (!msg) 193*4e69fc22SAndras Domokos goto out; 194*4e69fc22SAndras Domokos buf = kmalloc(alloc_size, GFP_KERNEL); 195*4e69fc22SAndras Domokos if (!buf) { 196*4e69fc22SAndras Domokos hsi_free_msg(msg); 197*4e69fc22SAndras Domokos goto out; 198*4e69fc22SAndras Domokos } 199*4e69fc22SAndras Domokos sg_init_one(msg->sgt.sgl, buf, alloc_size); 200*4e69fc22SAndras Domokos /* Ignore false positive, due to sg pointer handling */ 201*4e69fc22SAndras Domokos kmemleak_ignore(buf); 202*4e69fc22SAndras Domokos 203*4e69fc22SAndras Domokos return msg; 204*4e69fc22SAndras Domokos out: 205*4e69fc22SAndras Domokos return NULL; 206*4e69fc22SAndras Domokos } 207*4e69fc22SAndras Domokos 208*4e69fc22SAndras Domokos static inline int hsc_msgs_alloc(struct hsc_channel *channel) 209*4e69fc22SAndras Domokos { 210*4e69fc22SAndras Domokos struct hsi_msg *msg; 211*4e69fc22SAndras Domokos int i; 212*4e69fc22SAndras Domokos 213*4e69fc22SAndras Domokos for (i = 0; i < HSC_MSGS; i++) { 214*4e69fc22SAndras Domokos msg = hsc_msg_alloc(max_data_size); 215*4e69fc22SAndras Domokos if (!msg) 216*4e69fc22SAndras Domokos goto out; 217*4e69fc22SAndras Domokos msg->channel = channel->ch; 218*4e69fc22SAndras Domokos list_add_tail(&msg->link, &channel->free_msgs_list); 219*4e69fc22SAndras Domokos } 220*4e69fc22SAndras Domokos 221*4e69fc22SAndras Domokos return 0; 222*4e69fc22SAndras Domokos out: 223*4e69fc22SAndras Domokos hsc_free_list(&channel->free_msgs_list); 224*4e69fc22SAndras Domokos 225*4e69fc22SAndras Domokos return -ENOMEM; 226*4e69fc22SAndras Domokos } 227*4e69fc22SAndras Domokos 228*4e69fc22SAndras Domokos static inline unsigned int hsc_msg_len_get(struct hsi_msg *msg) 229*4e69fc22SAndras Domokos { 230*4e69fc22SAndras Domokos return msg->sgt.sgl->length; 231*4e69fc22SAndras Domokos } 232*4e69fc22SAndras Domokos 233*4e69fc22SAndras Domokos static inline void hsc_msg_len_set(struct hsi_msg *msg, unsigned int len) 234*4e69fc22SAndras Domokos { 235*4e69fc22SAndras Domokos msg->sgt.sgl->length = len; 236*4e69fc22SAndras Domokos } 237*4e69fc22SAndras Domokos 238*4e69fc22SAndras Domokos static void hsc_rx_completed(struct hsi_msg *msg) 239*4e69fc22SAndras Domokos { 240*4e69fc22SAndras Domokos struct hsc_client_data *cl_data = hsi_client_drvdata(msg->cl); 241*4e69fc22SAndras Domokos struct hsc_channel *channel = cl_data->channels + msg->channel; 242*4e69fc22SAndras Domokos 243*4e69fc22SAndras Domokos if (test_bit(HSC_CH_READ, &channel->flags)) { 244*4e69fc22SAndras Domokos hsc_add_tail(channel, msg, &channel->rx_msgs_queue); 245*4e69fc22SAndras Domokos wake_up(&channel->rx_wait); 246*4e69fc22SAndras Domokos } else { 247*4e69fc22SAndras Domokos hsc_add_tail(channel, msg, &channel->free_msgs_list); 248*4e69fc22SAndras Domokos } 249*4e69fc22SAndras Domokos } 250*4e69fc22SAndras Domokos 251*4e69fc22SAndras Domokos static void hsc_rx_msg_destructor(struct hsi_msg *msg) 252*4e69fc22SAndras Domokos { 253*4e69fc22SAndras Domokos msg->status = HSI_STATUS_ERROR; 254*4e69fc22SAndras Domokos hsc_msg_len_set(msg, 0); 255*4e69fc22SAndras Domokos hsc_rx_completed(msg); 256*4e69fc22SAndras Domokos } 257*4e69fc22SAndras Domokos 258*4e69fc22SAndras Domokos static void hsc_tx_completed(struct hsi_msg *msg) 259*4e69fc22SAndras Domokos { 260*4e69fc22SAndras Domokos struct hsc_client_data *cl_data = hsi_client_drvdata(msg->cl); 261*4e69fc22SAndras Domokos struct hsc_channel *channel = cl_data->channels + msg->channel; 262*4e69fc22SAndras Domokos 263*4e69fc22SAndras Domokos if (test_bit(HSC_CH_WRITE, &channel->flags)) { 264*4e69fc22SAndras Domokos hsc_add_tail(channel, msg, &channel->tx_msgs_queue); 265*4e69fc22SAndras Domokos wake_up(&channel->tx_wait); 266*4e69fc22SAndras Domokos } else { 267*4e69fc22SAndras Domokos hsc_add_tail(channel, msg, &channel->free_msgs_list); 268*4e69fc22SAndras Domokos } 269*4e69fc22SAndras Domokos } 270*4e69fc22SAndras Domokos 271*4e69fc22SAndras Domokos static void hsc_tx_msg_destructor(struct hsi_msg *msg) 272*4e69fc22SAndras Domokos { 273*4e69fc22SAndras Domokos msg->status = HSI_STATUS_ERROR; 274*4e69fc22SAndras Domokos hsc_msg_len_set(msg, 0); 275*4e69fc22SAndras Domokos hsc_tx_completed(msg); 276*4e69fc22SAndras Domokos } 277*4e69fc22SAndras Domokos 278*4e69fc22SAndras Domokos static void hsc_break_req_destructor(struct hsi_msg *msg) 279*4e69fc22SAndras Domokos { 280*4e69fc22SAndras Domokos struct hsc_client_data *cl_data = hsi_client_drvdata(msg->cl); 281*4e69fc22SAndras Domokos 282*4e69fc22SAndras Domokos hsi_free_msg(msg); 283*4e69fc22SAndras Domokos clear_bit(HSC_RXBREAK, &cl_data->flags); 284*4e69fc22SAndras Domokos } 285*4e69fc22SAndras Domokos 286*4e69fc22SAndras Domokos static void hsc_break_received(struct hsi_msg *msg) 287*4e69fc22SAndras Domokos { 288*4e69fc22SAndras Domokos struct hsc_client_data *cl_data = hsi_client_drvdata(msg->cl); 289*4e69fc22SAndras Domokos struct hsc_channel *channel = cl_data->channels; 290*4e69fc22SAndras Domokos int i, ret; 291*4e69fc22SAndras Domokos 292*4e69fc22SAndras Domokos /* Broadcast HWBREAK on all channels */ 293*4e69fc22SAndras Domokos for (i = 0; i < HSC_DEVS; i++, channel++) { 294*4e69fc22SAndras Domokos struct hsi_msg *msg2; 295*4e69fc22SAndras Domokos 296*4e69fc22SAndras Domokos if (!test_bit(HSC_CH_READ, &channel->flags)) 297*4e69fc22SAndras Domokos continue; 298*4e69fc22SAndras Domokos msg2 = hsc_get_first_msg(channel, &channel->free_msgs_list); 299*4e69fc22SAndras Domokos if (!msg2) 300*4e69fc22SAndras Domokos continue; 301*4e69fc22SAndras Domokos clear_bit(HSC_CH_READ, &channel->flags); 302*4e69fc22SAndras Domokos hsc_msg_len_set(msg2, 0); 303*4e69fc22SAndras Domokos msg2->status = HSI_STATUS_COMPLETED; 304*4e69fc22SAndras Domokos hsc_add_tail(channel, msg2, &channel->rx_msgs_queue); 305*4e69fc22SAndras Domokos wake_up(&channel->rx_wait); 306*4e69fc22SAndras Domokos } 307*4e69fc22SAndras Domokos hsi_flush(msg->cl); 308*4e69fc22SAndras Domokos ret = hsi_async_read(msg->cl, msg); 309*4e69fc22SAndras Domokos if (ret < 0) 310*4e69fc22SAndras Domokos hsc_break_req_destructor(msg); 311*4e69fc22SAndras Domokos } 312*4e69fc22SAndras Domokos 313*4e69fc22SAndras Domokos static int hsc_break_request(struct hsi_client *cl) 314*4e69fc22SAndras Domokos { 315*4e69fc22SAndras Domokos struct hsc_client_data *cl_data = hsi_client_drvdata(cl); 316*4e69fc22SAndras Domokos struct hsi_msg *msg; 317*4e69fc22SAndras Domokos int ret; 318*4e69fc22SAndras Domokos 319*4e69fc22SAndras Domokos if (test_and_set_bit(HSC_RXBREAK, &cl_data->flags)) 320*4e69fc22SAndras Domokos return -EBUSY; 321*4e69fc22SAndras Domokos 322*4e69fc22SAndras Domokos msg = hsi_alloc_msg(0, GFP_KERNEL); 323*4e69fc22SAndras Domokos if (!msg) { 324*4e69fc22SAndras Domokos clear_bit(HSC_RXBREAK, &cl_data->flags); 325*4e69fc22SAndras Domokos return -ENOMEM; 326*4e69fc22SAndras Domokos } 327*4e69fc22SAndras Domokos msg->break_frame = 1; 328*4e69fc22SAndras Domokos msg->complete = hsc_break_received; 329*4e69fc22SAndras Domokos msg->destructor = hsc_break_req_destructor; 330*4e69fc22SAndras Domokos ret = hsi_async_read(cl, msg); 331*4e69fc22SAndras Domokos if (ret < 0) 332*4e69fc22SAndras Domokos hsc_break_req_destructor(msg); 333*4e69fc22SAndras Domokos 334*4e69fc22SAndras Domokos return ret; 335*4e69fc22SAndras Domokos } 336*4e69fc22SAndras Domokos 337*4e69fc22SAndras Domokos static int hsc_break_send(struct hsi_client *cl) 338*4e69fc22SAndras Domokos { 339*4e69fc22SAndras Domokos struct hsi_msg *msg; 340*4e69fc22SAndras Domokos int ret; 341*4e69fc22SAndras Domokos 342*4e69fc22SAndras Domokos msg = hsi_alloc_msg(0, GFP_ATOMIC); 343*4e69fc22SAndras Domokos if (!msg) 344*4e69fc22SAndras Domokos return -ENOMEM; 345*4e69fc22SAndras Domokos msg->break_frame = 1; 346*4e69fc22SAndras Domokos msg->complete = hsi_free_msg; 347*4e69fc22SAndras Domokos msg->destructor = hsi_free_msg; 348*4e69fc22SAndras Domokos ret = hsi_async_write(cl, msg); 349*4e69fc22SAndras Domokos if (ret < 0) 350*4e69fc22SAndras Domokos hsi_free_msg(msg); 351*4e69fc22SAndras Domokos 352*4e69fc22SAndras Domokos return ret; 353*4e69fc22SAndras Domokos } 354*4e69fc22SAndras Domokos 355*4e69fc22SAndras Domokos static int hsc_rx_set(struct hsi_client *cl, struct hsc_rx_config *rxc) 356*4e69fc22SAndras Domokos { 357*4e69fc22SAndras Domokos struct hsi_config tmp; 358*4e69fc22SAndras Domokos int ret; 359*4e69fc22SAndras Domokos 360*4e69fc22SAndras Domokos if ((rxc->mode != HSI_MODE_STREAM) && (rxc->mode != HSI_MODE_FRAME)) 361*4e69fc22SAndras Domokos return -EINVAL; 362*4e69fc22SAndras Domokos if ((rxc->channels == 0) || (rxc->channels > HSC_DEVS)) 363*4e69fc22SAndras Domokos return -EINVAL; 364*4e69fc22SAndras Domokos if (rxc->channels & (rxc->channels - 1)) 365*4e69fc22SAndras Domokos return -EINVAL; 366*4e69fc22SAndras Domokos if ((rxc->flow != HSI_FLOW_SYNC) && (rxc->flow != HSI_FLOW_PIPE)) 367*4e69fc22SAndras Domokos return -EINVAL; 368*4e69fc22SAndras Domokos tmp = cl->rx_cfg; 369*4e69fc22SAndras Domokos cl->rx_cfg.mode = rxc->mode; 370*4e69fc22SAndras Domokos cl->rx_cfg.channels = rxc->channels; 371*4e69fc22SAndras Domokos cl->rx_cfg.flow = rxc->flow; 372*4e69fc22SAndras Domokos ret = hsi_setup(cl); 373*4e69fc22SAndras Domokos if (ret < 0) { 374*4e69fc22SAndras Domokos cl->rx_cfg = tmp; 375*4e69fc22SAndras Domokos return ret; 376*4e69fc22SAndras Domokos } 377*4e69fc22SAndras Domokos if (rxc->mode == HSI_MODE_FRAME) 378*4e69fc22SAndras Domokos hsc_break_request(cl); 379*4e69fc22SAndras Domokos 380*4e69fc22SAndras Domokos return ret; 381*4e69fc22SAndras Domokos } 382*4e69fc22SAndras Domokos 383*4e69fc22SAndras Domokos static inline void hsc_rx_get(struct hsi_client *cl, struct hsc_rx_config *rxc) 384*4e69fc22SAndras Domokos { 385*4e69fc22SAndras Domokos rxc->mode = cl->rx_cfg.mode; 386*4e69fc22SAndras Domokos rxc->channels = cl->rx_cfg.channels; 387*4e69fc22SAndras Domokos rxc->flow = cl->rx_cfg.flow; 388*4e69fc22SAndras Domokos } 389*4e69fc22SAndras Domokos 390*4e69fc22SAndras Domokos static int hsc_tx_set(struct hsi_client *cl, struct hsc_tx_config *txc) 391*4e69fc22SAndras Domokos { 392*4e69fc22SAndras Domokos struct hsi_config tmp; 393*4e69fc22SAndras Domokos int ret; 394*4e69fc22SAndras Domokos 395*4e69fc22SAndras Domokos if ((txc->mode != HSI_MODE_STREAM) && (txc->mode != HSI_MODE_FRAME)) 396*4e69fc22SAndras Domokos return -EINVAL; 397*4e69fc22SAndras Domokos if ((txc->channels == 0) || (txc->channels > HSC_DEVS)) 398*4e69fc22SAndras Domokos return -EINVAL; 399*4e69fc22SAndras Domokos if (txc->channels & (txc->channels - 1)) 400*4e69fc22SAndras Domokos return -EINVAL; 401*4e69fc22SAndras Domokos if ((txc->arb_mode != HSI_ARB_RR) && (txc->arb_mode != HSI_ARB_PRIO)) 402*4e69fc22SAndras Domokos return -EINVAL; 403*4e69fc22SAndras Domokos tmp = cl->tx_cfg; 404*4e69fc22SAndras Domokos cl->tx_cfg.mode = txc->mode; 405*4e69fc22SAndras Domokos cl->tx_cfg.channels = txc->channels; 406*4e69fc22SAndras Domokos cl->tx_cfg.speed = txc->speed; 407*4e69fc22SAndras Domokos cl->tx_cfg.arb_mode = txc->arb_mode; 408*4e69fc22SAndras Domokos ret = hsi_setup(cl); 409*4e69fc22SAndras Domokos if (ret < 0) { 410*4e69fc22SAndras Domokos cl->tx_cfg = tmp; 411*4e69fc22SAndras Domokos return ret; 412*4e69fc22SAndras Domokos } 413*4e69fc22SAndras Domokos 414*4e69fc22SAndras Domokos return ret; 415*4e69fc22SAndras Domokos } 416*4e69fc22SAndras Domokos 417*4e69fc22SAndras Domokos static inline void hsc_tx_get(struct hsi_client *cl, struct hsc_tx_config *txc) 418*4e69fc22SAndras Domokos { 419*4e69fc22SAndras Domokos txc->mode = cl->tx_cfg.mode; 420*4e69fc22SAndras Domokos txc->channels = cl->tx_cfg.channels; 421*4e69fc22SAndras Domokos txc->speed = cl->tx_cfg.speed; 422*4e69fc22SAndras Domokos txc->arb_mode = cl->tx_cfg.arb_mode; 423*4e69fc22SAndras Domokos } 424*4e69fc22SAndras Domokos 425*4e69fc22SAndras Domokos static ssize_t hsc_read(struct file *file, char __user *buf, size_t len, 426*4e69fc22SAndras Domokos loff_t *ppos __maybe_unused) 427*4e69fc22SAndras Domokos { 428*4e69fc22SAndras Domokos struct hsc_channel *channel = file->private_data; 429*4e69fc22SAndras Domokos struct hsi_msg *msg; 430*4e69fc22SAndras Domokos ssize_t ret; 431*4e69fc22SAndras Domokos 432*4e69fc22SAndras Domokos if (len == 0) 433*4e69fc22SAndras Domokos return 0; 434*4e69fc22SAndras Domokos if (!IS_ALIGNED(len, sizeof(u32))) 435*4e69fc22SAndras Domokos return -EINVAL; 436*4e69fc22SAndras Domokos if (len > max_data_size) 437*4e69fc22SAndras Domokos len = max_data_size; 438*4e69fc22SAndras Domokos if (channel->ch >= channel->cl->rx_cfg.channels) 439*4e69fc22SAndras Domokos return -ECHRNG; 440*4e69fc22SAndras Domokos if (test_and_set_bit(HSC_CH_READ, &channel->flags)) 441*4e69fc22SAndras Domokos return -EBUSY; 442*4e69fc22SAndras Domokos msg = hsc_get_first_msg(channel, &channel->free_msgs_list); 443*4e69fc22SAndras Domokos if (!msg) { 444*4e69fc22SAndras Domokos ret = -ENOSPC; 445*4e69fc22SAndras Domokos goto out; 446*4e69fc22SAndras Domokos } 447*4e69fc22SAndras Domokos hsc_msg_len_set(msg, len); 448*4e69fc22SAndras Domokos msg->complete = hsc_rx_completed; 449*4e69fc22SAndras Domokos msg->destructor = hsc_rx_msg_destructor; 450*4e69fc22SAndras Domokos ret = hsi_async_read(channel->cl, msg); 451*4e69fc22SAndras Domokos if (ret < 0) { 452*4e69fc22SAndras Domokos hsc_add_tail(channel, msg, &channel->free_msgs_list); 453*4e69fc22SAndras Domokos goto out; 454*4e69fc22SAndras Domokos } 455*4e69fc22SAndras Domokos 456*4e69fc22SAndras Domokos ret = wait_event_interruptible(channel->rx_wait, 457*4e69fc22SAndras Domokos !list_empty(&channel->rx_msgs_queue)); 458*4e69fc22SAndras Domokos if (ret < 0) { 459*4e69fc22SAndras Domokos clear_bit(HSC_CH_READ, &channel->flags); 460*4e69fc22SAndras Domokos hsi_flush(channel->cl); 461*4e69fc22SAndras Domokos return -EINTR; 462*4e69fc22SAndras Domokos } 463*4e69fc22SAndras Domokos 464*4e69fc22SAndras Domokos msg = hsc_get_first_msg(channel, &channel->rx_msgs_queue); 465*4e69fc22SAndras Domokos if (msg) { 466*4e69fc22SAndras Domokos if (msg->status != HSI_STATUS_ERROR) { 467*4e69fc22SAndras Domokos ret = copy_to_user((void __user *)buf, 468*4e69fc22SAndras Domokos sg_virt(msg->sgt.sgl), hsc_msg_len_get(msg)); 469*4e69fc22SAndras Domokos if (ret) 470*4e69fc22SAndras Domokos ret = -EFAULT; 471*4e69fc22SAndras Domokos else 472*4e69fc22SAndras Domokos ret = hsc_msg_len_get(msg); 473*4e69fc22SAndras Domokos } else { 474*4e69fc22SAndras Domokos ret = -EIO; 475*4e69fc22SAndras Domokos } 476*4e69fc22SAndras Domokos hsc_add_tail(channel, msg, &channel->free_msgs_list); 477*4e69fc22SAndras Domokos } 478*4e69fc22SAndras Domokos out: 479*4e69fc22SAndras Domokos clear_bit(HSC_CH_READ, &channel->flags); 480*4e69fc22SAndras Domokos 481*4e69fc22SAndras Domokos return ret; 482*4e69fc22SAndras Domokos } 483*4e69fc22SAndras Domokos 484*4e69fc22SAndras Domokos static ssize_t hsc_write(struct file *file, const char __user *buf, size_t len, 485*4e69fc22SAndras Domokos loff_t *ppos __maybe_unused) 486*4e69fc22SAndras Domokos { 487*4e69fc22SAndras Domokos struct hsc_channel *channel = file->private_data; 488*4e69fc22SAndras Domokos struct hsi_msg *msg; 489*4e69fc22SAndras Domokos ssize_t ret; 490*4e69fc22SAndras Domokos 491*4e69fc22SAndras Domokos if ((len == 0) || !IS_ALIGNED(len, sizeof(u32))) 492*4e69fc22SAndras Domokos return -EINVAL; 493*4e69fc22SAndras Domokos if (len > max_data_size) 494*4e69fc22SAndras Domokos len = max_data_size; 495*4e69fc22SAndras Domokos if (channel->ch >= channel->cl->tx_cfg.channels) 496*4e69fc22SAndras Domokos return -ECHRNG; 497*4e69fc22SAndras Domokos if (test_and_set_bit(HSC_CH_WRITE, &channel->flags)) 498*4e69fc22SAndras Domokos return -EBUSY; 499*4e69fc22SAndras Domokos msg = hsc_get_first_msg(channel, &channel->free_msgs_list); 500*4e69fc22SAndras Domokos if (!msg) { 501*4e69fc22SAndras Domokos clear_bit(HSC_CH_WRITE, &channel->flags); 502*4e69fc22SAndras Domokos return -ENOSPC; 503*4e69fc22SAndras Domokos } 504*4e69fc22SAndras Domokos if (copy_from_user(sg_virt(msg->sgt.sgl), (void __user *)buf, len)) { 505*4e69fc22SAndras Domokos ret = -EFAULT; 506*4e69fc22SAndras Domokos goto out; 507*4e69fc22SAndras Domokos } 508*4e69fc22SAndras Domokos hsc_msg_len_set(msg, len); 509*4e69fc22SAndras Domokos msg->complete = hsc_tx_completed; 510*4e69fc22SAndras Domokos msg->destructor = hsc_tx_msg_destructor; 511*4e69fc22SAndras Domokos ret = hsi_async_write(channel->cl, msg); 512*4e69fc22SAndras Domokos if (ret < 0) 513*4e69fc22SAndras Domokos goto out; 514*4e69fc22SAndras Domokos 515*4e69fc22SAndras Domokos ret = wait_event_interruptible(channel->tx_wait, 516*4e69fc22SAndras Domokos !list_empty(&channel->tx_msgs_queue)); 517*4e69fc22SAndras Domokos if (ret < 0) { 518*4e69fc22SAndras Domokos clear_bit(HSC_CH_WRITE, &channel->flags); 519*4e69fc22SAndras Domokos hsi_flush(channel->cl); 520*4e69fc22SAndras Domokos return -EINTR; 521*4e69fc22SAndras Domokos } 522*4e69fc22SAndras Domokos 523*4e69fc22SAndras Domokos msg = hsc_get_first_msg(channel, &channel->tx_msgs_queue); 524*4e69fc22SAndras Domokos if (msg) { 525*4e69fc22SAndras Domokos if (msg->status == HSI_STATUS_ERROR) 526*4e69fc22SAndras Domokos ret = -EIO; 527*4e69fc22SAndras Domokos else 528*4e69fc22SAndras Domokos ret = hsc_msg_len_get(msg); 529*4e69fc22SAndras Domokos 530*4e69fc22SAndras Domokos hsc_add_tail(channel, msg, &channel->free_msgs_list); 531*4e69fc22SAndras Domokos } 532*4e69fc22SAndras Domokos out: 533*4e69fc22SAndras Domokos clear_bit(HSC_CH_WRITE, &channel->flags); 534*4e69fc22SAndras Domokos 535*4e69fc22SAndras Domokos return ret; 536*4e69fc22SAndras Domokos } 537*4e69fc22SAndras Domokos 538*4e69fc22SAndras Domokos static long hsc_ioctl(struct file *file, unsigned int cmd, unsigned long arg) 539*4e69fc22SAndras Domokos { 540*4e69fc22SAndras Domokos struct hsc_channel *channel = file->private_data; 541*4e69fc22SAndras Domokos unsigned int state; 542*4e69fc22SAndras Domokos struct hsc_rx_config rxc; 543*4e69fc22SAndras Domokos struct hsc_tx_config txc; 544*4e69fc22SAndras Domokos long ret = 0; 545*4e69fc22SAndras Domokos 546*4e69fc22SAndras Domokos switch (cmd) { 547*4e69fc22SAndras Domokos case HSC_RESET: 548*4e69fc22SAndras Domokos hsi_flush(channel->cl); 549*4e69fc22SAndras Domokos break; 550*4e69fc22SAndras Domokos case HSC_SET_PM: 551*4e69fc22SAndras Domokos if (copy_from_user(&state, (void __user *)arg, sizeof(state))) 552*4e69fc22SAndras Domokos return -EFAULT; 553*4e69fc22SAndras Domokos if (state == HSC_PM_DISABLE) { 554*4e69fc22SAndras Domokos if (test_and_set_bit(HSC_CH_WLINE, &channel->flags)) 555*4e69fc22SAndras Domokos return -EINVAL; 556*4e69fc22SAndras Domokos ret = hsi_start_tx(channel->cl); 557*4e69fc22SAndras Domokos } else if (state == HSC_PM_ENABLE) { 558*4e69fc22SAndras Domokos if (!test_and_clear_bit(HSC_CH_WLINE, &channel->flags)) 559*4e69fc22SAndras Domokos return -EINVAL; 560*4e69fc22SAndras Domokos ret = hsi_stop_tx(channel->cl); 561*4e69fc22SAndras Domokos } else { 562*4e69fc22SAndras Domokos ret = -EINVAL; 563*4e69fc22SAndras Domokos } 564*4e69fc22SAndras Domokos break; 565*4e69fc22SAndras Domokos case HSC_SEND_BREAK: 566*4e69fc22SAndras Domokos return hsc_break_send(channel->cl); 567*4e69fc22SAndras Domokos case HSC_SET_RX: 568*4e69fc22SAndras Domokos if (copy_from_user(&rxc, (void __user *)arg, sizeof(rxc))) 569*4e69fc22SAndras Domokos return -EFAULT; 570*4e69fc22SAndras Domokos return hsc_rx_set(channel->cl, &rxc); 571*4e69fc22SAndras Domokos case HSC_GET_RX: 572*4e69fc22SAndras Domokos hsc_rx_get(channel->cl, &rxc); 573*4e69fc22SAndras Domokos if (copy_to_user((void __user *)arg, &rxc, sizeof(rxc))) 574*4e69fc22SAndras Domokos return -EFAULT; 575*4e69fc22SAndras Domokos break; 576*4e69fc22SAndras Domokos case HSC_SET_TX: 577*4e69fc22SAndras Domokos if (copy_from_user(&txc, (void __user *)arg, sizeof(txc))) 578*4e69fc22SAndras Domokos return -EFAULT; 579*4e69fc22SAndras Domokos return hsc_tx_set(channel->cl, &txc); 580*4e69fc22SAndras Domokos case HSC_GET_TX: 581*4e69fc22SAndras Domokos hsc_tx_get(channel->cl, &txc); 582*4e69fc22SAndras Domokos if (copy_to_user((void __user *)arg, &txc, sizeof(txc))) 583*4e69fc22SAndras Domokos return -EFAULT; 584*4e69fc22SAndras Domokos break; 585*4e69fc22SAndras Domokos default: 586*4e69fc22SAndras Domokos return -ENOIOCTLCMD; 587*4e69fc22SAndras Domokos } 588*4e69fc22SAndras Domokos 589*4e69fc22SAndras Domokos return ret; 590*4e69fc22SAndras Domokos } 591*4e69fc22SAndras Domokos 592*4e69fc22SAndras Domokos static inline void __hsc_port_release(struct hsc_client_data *cl_data) 593*4e69fc22SAndras Domokos { 594*4e69fc22SAndras Domokos BUG_ON(cl_data->usecnt == 0); 595*4e69fc22SAndras Domokos 596*4e69fc22SAndras Domokos if (--cl_data->usecnt == 0) { 597*4e69fc22SAndras Domokos hsi_flush(cl_data->cl); 598*4e69fc22SAndras Domokos hsi_release_port(cl_data->cl); 599*4e69fc22SAndras Domokos } 600*4e69fc22SAndras Domokos } 601*4e69fc22SAndras Domokos 602*4e69fc22SAndras Domokos static int hsc_open(struct inode *inode, struct file *file) 603*4e69fc22SAndras Domokos { 604*4e69fc22SAndras Domokos struct hsc_client_data *cl_data; 605*4e69fc22SAndras Domokos struct hsc_channel *channel; 606*4e69fc22SAndras Domokos int ret = 0; 607*4e69fc22SAndras Domokos 608*4e69fc22SAndras Domokos pr_debug("open, minor = %d\n", iminor(inode)); 609*4e69fc22SAndras Domokos 610*4e69fc22SAndras Domokos cl_data = container_of(inode->i_cdev, struct hsc_client_data, cdev); 611*4e69fc22SAndras Domokos mutex_lock(&cl_data->lock); 612*4e69fc22SAndras Domokos channel = cl_data->channels + (iminor(inode) & HSC_CH_MASK); 613*4e69fc22SAndras Domokos 614*4e69fc22SAndras Domokos if (test_and_set_bit(HSC_CH_OPEN, &channel->flags)) { 615*4e69fc22SAndras Domokos ret = -EBUSY; 616*4e69fc22SAndras Domokos goto out; 617*4e69fc22SAndras Domokos } 618*4e69fc22SAndras Domokos /* 619*4e69fc22SAndras Domokos * Check if we have already claimed the port associated to the HSI 620*4e69fc22SAndras Domokos * client. If not then try to claim it, else increase its refcount 621*4e69fc22SAndras Domokos */ 622*4e69fc22SAndras Domokos if (cl_data->usecnt == 0) { 623*4e69fc22SAndras Domokos ret = hsi_claim_port(cl_data->cl, 0); 624*4e69fc22SAndras Domokos if (ret < 0) 625*4e69fc22SAndras Domokos goto out; 626*4e69fc22SAndras Domokos hsi_setup(cl_data->cl); 627*4e69fc22SAndras Domokos } 628*4e69fc22SAndras Domokos cl_data->usecnt++; 629*4e69fc22SAndras Domokos 630*4e69fc22SAndras Domokos ret = hsc_msgs_alloc(channel); 631*4e69fc22SAndras Domokos if (ret < 0) { 632*4e69fc22SAndras Domokos __hsc_port_release(cl_data); 633*4e69fc22SAndras Domokos goto out; 634*4e69fc22SAndras Domokos } 635*4e69fc22SAndras Domokos 636*4e69fc22SAndras Domokos file->private_data = channel; 637*4e69fc22SAndras Domokos mutex_unlock(&cl_data->lock); 638*4e69fc22SAndras Domokos 639*4e69fc22SAndras Domokos return ret; 640*4e69fc22SAndras Domokos out: 641*4e69fc22SAndras Domokos mutex_unlock(&cl_data->lock); 642*4e69fc22SAndras Domokos 643*4e69fc22SAndras Domokos return ret; 644*4e69fc22SAndras Domokos } 645*4e69fc22SAndras Domokos 646*4e69fc22SAndras Domokos static int hsc_release(struct inode *inode __maybe_unused, struct file *file) 647*4e69fc22SAndras Domokos { 648*4e69fc22SAndras Domokos struct hsc_channel *channel = file->private_data; 649*4e69fc22SAndras Domokos struct hsc_client_data *cl_data = channel->cl_data; 650*4e69fc22SAndras Domokos 651*4e69fc22SAndras Domokos mutex_lock(&cl_data->lock); 652*4e69fc22SAndras Domokos file->private_data = NULL; 653*4e69fc22SAndras Domokos if (test_and_clear_bit(HSC_CH_WLINE, &channel->flags)) 654*4e69fc22SAndras Domokos hsi_stop_tx(channel->cl); 655*4e69fc22SAndras Domokos __hsc_port_release(cl_data); 656*4e69fc22SAndras Domokos hsc_reset_list(channel, &channel->rx_msgs_queue); 657*4e69fc22SAndras Domokos hsc_reset_list(channel, &channel->tx_msgs_queue); 658*4e69fc22SAndras Domokos hsc_reset_list(channel, &channel->free_msgs_list); 659*4e69fc22SAndras Domokos clear_bit(HSC_CH_READ, &channel->flags); 660*4e69fc22SAndras Domokos clear_bit(HSC_CH_WRITE, &channel->flags); 661*4e69fc22SAndras Domokos clear_bit(HSC_CH_OPEN, &channel->flags); 662*4e69fc22SAndras Domokos wake_up(&channel->rx_wait); 663*4e69fc22SAndras Domokos wake_up(&channel->tx_wait); 664*4e69fc22SAndras Domokos mutex_unlock(&cl_data->lock); 665*4e69fc22SAndras Domokos 666*4e69fc22SAndras Domokos return 0; 667*4e69fc22SAndras Domokos } 668*4e69fc22SAndras Domokos 669*4e69fc22SAndras Domokos static const struct file_operations hsc_fops = { 670*4e69fc22SAndras Domokos .owner = THIS_MODULE, 671*4e69fc22SAndras Domokos .read = hsc_read, 672*4e69fc22SAndras Domokos .write = hsc_write, 673*4e69fc22SAndras Domokos .unlocked_ioctl = hsc_ioctl, 674*4e69fc22SAndras Domokos .open = hsc_open, 675*4e69fc22SAndras Domokos .release = hsc_release, 676*4e69fc22SAndras Domokos }; 677*4e69fc22SAndras Domokos 678*4e69fc22SAndras Domokos static void __devinit hsc_channel_init(struct hsc_channel *channel) 679*4e69fc22SAndras Domokos { 680*4e69fc22SAndras Domokos init_waitqueue_head(&channel->rx_wait); 681*4e69fc22SAndras Domokos init_waitqueue_head(&channel->tx_wait); 682*4e69fc22SAndras Domokos spin_lock_init(&channel->lock); 683*4e69fc22SAndras Domokos INIT_LIST_HEAD(&channel->free_msgs_list); 684*4e69fc22SAndras Domokos INIT_LIST_HEAD(&channel->rx_msgs_queue); 685*4e69fc22SAndras Domokos INIT_LIST_HEAD(&channel->tx_msgs_queue); 686*4e69fc22SAndras Domokos } 687*4e69fc22SAndras Domokos 688*4e69fc22SAndras Domokos static int __devinit hsc_probe(struct device *dev) 689*4e69fc22SAndras Domokos { 690*4e69fc22SAndras Domokos const char devname[] = "hsi_char"; 691*4e69fc22SAndras Domokos struct hsc_client_data *cl_data; 692*4e69fc22SAndras Domokos struct hsc_channel *channel; 693*4e69fc22SAndras Domokos struct hsi_client *cl = to_hsi_client(dev); 694*4e69fc22SAndras Domokos unsigned int hsc_baseminor; 695*4e69fc22SAndras Domokos dev_t hsc_dev; 696*4e69fc22SAndras Domokos int ret; 697*4e69fc22SAndras Domokos int i; 698*4e69fc22SAndras Domokos 699*4e69fc22SAndras Domokos cl_data = kzalloc(sizeof(*cl_data), GFP_KERNEL); 700*4e69fc22SAndras Domokos if (!cl_data) { 701*4e69fc22SAndras Domokos dev_err(dev, "Could not allocate hsc_client_data\n"); 702*4e69fc22SAndras Domokos return -ENOMEM; 703*4e69fc22SAndras Domokos } 704*4e69fc22SAndras Domokos hsc_baseminor = HSC_BASEMINOR(hsi_id(cl), hsi_port_id(cl)); 705*4e69fc22SAndras Domokos if (!hsc_major) { 706*4e69fc22SAndras Domokos ret = alloc_chrdev_region(&hsc_dev, hsc_baseminor, 707*4e69fc22SAndras Domokos HSC_DEVS, devname); 708*4e69fc22SAndras Domokos if (ret > 0) 709*4e69fc22SAndras Domokos hsc_major = MAJOR(hsc_dev); 710*4e69fc22SAndras Domokos } else { 711*4e69fc22SAndras Domokos hsc_dev = MKDEV(hsc_major, hsc_baseminor); 712*4e69fc22SAndras Domokos ret = register_chrdev_region(hsc_dev, HSC_DEVS, devname); 713*4e69fc22SAndras Domokos } 714*4e69fc22SAndras Domokos if (ret < 0) { 715*4e69fc22SAndras Domokos dev_err(dev, "Device %s allocation failed %d\n", 716*4e69fc22SAndras Domokos hsc_major ? "minor" : "major", ret); 717*4e69fc22SAndras Domokos goto out1; 718*4e69fc22SAndras Domokos } 719*4e69fc22SAndras Domokos mutex_init(&cl_data->lock); 720*4e69fc22SAndras Domokos hsi_client_set_drvdata(cl, cl_data); 721*4e69fc22SAndras Domokos cdev_init(&cl_data->cdev, &hsc_fops); 722*4e69fc22SAndras Domokos cl_data->cdev.owner = THIS_MODULE; 723*4e69fc22SAndras Domokos cl_data->cl = cl; 724*4e69fc22SAndras Domokos for (i = 0, channel = cl_data->channels; i < HSC_DEVS; i++, channel++) { 725*4e69fc22SAndras Domokos hsc_channel_init(channel); 726*4e69fc22SAndras Domokos channel->ch = i; 727*4e69fc22SAndras Domokos channel->cl = cl; 728*4e69fc22SAndras Domokos channel->cl_data = cl_data; 729*4e69fc22SAndras Domokos } 730*4e69fc22SAndras Domokos 731*4e69fc22SAndras Domokos /* 1 hsi client -> N char devices (one for each channel) */ 732*4e69fc22SAndras Domokos ret = cdev_add(&cl_data->cdev, hsc_dev, HSC_DEVS); 733*4e69fc22SAndras Domokos if (ret) { 734*4e69fc22SAndras Domokos dev_err(dev, "Could not add char device %d\n", ret); 735*4e69fc22SAndras Domokos goto out2; 736*4e69fc22SAndras Domokos } 737*4e69fc22SAndras Domokos 738*4e69fc22SAndras Domokos return 0; 739*4e69fc22SAndras Domokos out2: 740*4e69fc22SAndras Domokos unregister_chrdev_region(hsc_dev, HSC_DEVS); 741*4e69fc22SAndras Domokos out1: 742*4e69fc22SAndras Domokos kfree(cl_data); 743*4e69fc22SAndras Domokos 744*4e69fc22SAndras Domokos return ret; 745*4e69fc22SAndras Domokos } 746*4e69fc22SAndras Domokos 747*4e69fc22SAndras Domokos static int __devexit hsc_remove(struct device *dev) 748*4e69fc22SAndras Domokos { 749*4e69fc22SAndras Domokos struct hsi_client *cl = to_hsi_client(dev); 750*4e69fc22SAndras Domokos struct hsc_client_data *cl_data = hsi_client_drvdata(cl); 751*4e69fc22SAndras Domokos dev_t hsc_dev = cl_data->cdev.dev; 752*4e69fc22SAndras Domokos 753*4e69fc22SAndras Domokos cdev_del(&cl_data->cdev); 754*4e69fc22SAndras Domokos unregister_chrdev_region(hsc_dev, HSC_DEVS); 755*4e69fc22SAndras Domokos hsi_client_set_drvdata(cl, NULL); 756*4e69fc22SAndras Domokos kfree(cl_data); 757*4e69fc22SAndras Domokos 758*4e69fc22SAndras Domokos return 0; 759*4e69fc22SAndras Domokos } 760*4e69fc22SAndras Domokos 761*4e69fc22SAndras Domokos static struct hsi_client_driver hsc_driver = { 762*4e69fc22SAndras Domokos .driver = { 763*4e69fc22SAndras Domokos .name = "hsi_char", 764*4e69fc22SAndras Domokos .owner = THIS_MODULE, 765*4e69fc22SAndras Domokos .probe = hsc_probe, 766*4e69fc22SAndras Domokos .remove = __devexit_p(hsc_remove), 767*4e69fc22SAndras Domokos }, 768*4e69fc22SAndras Domokos }; 769*4e69fc22SAndras Domokos 770*4e69fc22SAndras Domokos static int __init hsc_init(void) 771*4e69fc22SAndras Domokos { 772*4e69fc22SAndras Domokos int ret; 773*4e69fc22SAndras Domokos 774*4e69fc22SAndras Domokos if ((max_data_size < 4) || (max_data_size > 0x10000) || 775*4e69fc22SAndras Domokos (max_data_size & (max_data_size - 1))) { 776*4e69fc22SAndras Domokos pr_err("Invalid max read/write data size"); 777*4e69fc22SAndras Domokos return -EINVAL; 778*4e69fc22SAndras Domokos } 779*4e69fc22SAndras Domokos 780*4e69fc22SAndras Domokos ret = hsi_register_client_driver(&hsc_driver); 781*4e69fc22SAndras Domokos if (ret) { 782*4e69fc22SAndras Domokos pr_err("Error while registering HSI/SSI driver %d", ret); 783*4e69fc22SAndras Domokos return ret; 784*4e69fc22SAndras Domokos } 785*4e69fc22SAndras Domokos 786*4e69fc22SAndras Domokos pr_info("HSI/SSI char device loaded\n"); 787*4e69fc22SAndras Domokos 788*4e69fc22SAndras Domokos return 0; 789*4e69fc22SAndras Domokos } 790*4e69fc22SAndras Domokos module_init(hsc_init); 791*4e69fc22SAndras Domokos 792*4e69fc22SAndras Domokos static void __exit hsc_exit(void) 793*4e69fc22SAndras Domokos { 794*4e69fc22SAndras Domokos hsi_unregister_client_driver(&hsc_driver); 795*4e69fc22SAndras Domokos pr_info("HSI char device removed\n"); 796*4e69fc22SAndras Domokos } 797*4e69fc22SAndras Domokos module_exit(hsc_exit); 798*4e69fc22SAndras Domokos 799*4e69fc22SAndras Domokos MODULE_AUTHOR("Andras Domokos <andras.domokos@nokia.com>"); 800*4e69fc22SAndras Domokos MODULE_ALIAS("hsi:hsi_char"); 801*4e69fc22SAndras Domokos MODULE_DESCRIPTION("HSI character device"); 802*4e69fc22SAndras Domokos MODULE_LICENSE("GPL v2"); 803