brintos

brintos / linux-shallow public Read only

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