Rev 462 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
420 | giacomo | 1 | /* |
2 | i2c-dev.c - i2c-bus driver, char device interface |
||
3 | |||
4 | Copyright (C) 1995-97 Simon G. Vogl |
||
5 | Copyright (C) 1998-99 Frodo Looijaard <frodol@dds.nl> |
||
6 | Copyright (C) 2003 Greg Kroah-Hartman <greg@kroah.com> |
||
7 | |||
8 | This program is free software; you can redistribute it and/or modify |
||
9 | it under the terms of the GNU General Public License as published by |
||
10 | the Free Software Foundation; either version 2 of the License, or |
||
11 | (at your option) any later version. |
||
12 | |||
13 | This program is distributed in the hope that it will be useful, |
||
14 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
||
15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||
16 | GNU General Public License for more details. |
||
17 | |||
18 | You should have received a copy of the GNU General Public License |
||
19 | along with this program; if not, write to the Free Software |
||
20 | Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
||
21 | */ |
||
22 | |||
23 | /* Note that this is a complete rewrite of Simon Vogl's i2c-dev module. |
||
24 | But I have used so much of his original code and ideas that it seems |
||
25 | only fair to recognize him as co-author -- Frodo */ |
||
26 | |||
27 | /* The I2C_RDWR ioctl code is written by Kolja Waschk <waschk@telos.de> */ |
||
28 | |||
29 | /* The devfs code is contributed by Philipp Matthias Hahn |
||
30 | <pmhahn@titan.lahn.de> */ |
||
31 | |||
32 | /* If you want debugging uncomment: */ |
||
462 | giacomo | 33 | #define DEBUG 1 |
420 | giacomo | 34 | |
458 | giacomo | 35 | #include <linuxcomp.h> |
36 | |||
420 | giacomo | 37 | #include <linux/kernel.h> |
38 | #include <linux/module.h> |
||
39 | #include <linux/fs.h> |
||
40 | #include <linux/slab.h> |
||
41 | #include <linux/smp_lock.h> |
||
42 | #include <linux/devfs_fs_kernel.h> |
||
43 | #include <linux/init.h> |
||
44 | #include <linux/i2c.h> |
||
45 | #include <linux/i2c-dev.h> |
||
46 | #include <asm/uaccess.h> |
||
47 | |||
48 | static struct i2c_client i2cdev_client_template; |
||
49 | |||
50 | struct i2c_dev { |
||
51 | int minor; |
||
52 | struct i2c_adapter *adap; |
||
53 | struct class_device class_dev; |
||
54 | struct completion released; /* FIXME, we need a class_device_unregister() */ |
||
55 | }; |
||
56 | #define to_i2c_dev(d) container_of(d, struct i2c_dev, class_dev) |
||
57 | |||
58 | #define I2C_MINORS 256 |
||
59 | static struct i2c_dev *i2c_dev_array[I2C_MINORS]; |
||
60 | static spinlock_t i2c_dev_array_lock = SPIN_LOCK_UNLOCKED; |
||
61 | |||
62 | struct i2c_dev *i2c_dev_get_by_minor(unsigned index) |
||
63 | { |
||
64 | struct i2c_dev *i2c_dev; |
||
65 | |||
66 | spin_lock(&i2c_dev_array_lock); |
||
67 | i2c_dev = i2c_dev_array[index]; |
||
68 | spin_unlock(&i2c_dev_array_lock); |
||
69 | return i2c_dev; |
||
70 | } |
||
71 | |||
72 | struct i2c_dev *i2c_dev_get_by_adapter(struct i2c_adapter *adap) |
||
73 | { |
||
74 | struct i2c_dev *i2c_dev = NULL; |
||
75 | int i; |
||
76 | |||
77 | spin_lock(&i2c_dev_array_lock); |
||
78 | for (i = 0; i < I2C_MINORS; ++i) { |
||
79 | if ((i2c_dev_array[i]) && |
||
80 | (i2c_dev_array[i]->adap == adap)) { |
||
81 | i2c_dev = i2c_dev_array[i]; |
||
82 | break; |
||
83 | } |
||
84 | } |
||
85 | spin_unlock(&i2c_dev_array_lock); |
||
86 | return i2c_dev; |
||
87 | } |
||
88 | |||
89 | static struct i2c_dev *get_free_i2c_dev(void) |
||
90 | { |
||
91 | struct i2c_dev *i2c_dev; |
||
92 | unsigned int i; |
||
93 | |||
94 | i2c_dev = kmalloc(sizeof(*i2c_dev), GFP_KERNEL); |
||
95 | if (!i2c_dev) |
||
96 | return ERR_PTR(-ENOMEM); |
||
97 | memset(i2c_dev, 0x00, sizeof(*i2c_dev)); |
||
98 | |||
99 | spin_lock(&i2c_dev_array_lock); |
||
100 | for (i = 0; i < I2C_MINORS; ++i) { |
||
101 | if (i2c_dev_array[i]) |
||
102 | continue; |
||
103 | i2c_dev->minor = i; |
||
104 | i2c_dev_array[i] = i2c_dev; |
||
105 | spin_unlock(&i2c_dev_array_lock); |
||
106 | return i2c_dev; |
||
107 | } |
||
108 | spin_unlock(&i2c_dev_array_lock); |
||
109 | kfree(i2c_dev); |
||
110 | return ERR_PTR(-ENODEV); |
||
111 | } |
||
112 | |||
113 | static void return_i2c_dev(struct i2c_dev *i2c_dev) |
||
114 | { |
||
115 | spin_lock(&i2c_dev_array_lock); |
||
116 | i2c_dev_array[i2c_dev->minor] = NULL; |
||
117 | spin_unlock(&i2c_dev_array_lock); |
||
118 | } |
||
119 | |||
120 | static ssize_t show_dev(struct class_device *class_dev, char *buf) |
||
121 | { |
||
122 | struct i2c_dev *i2c_dev = to_i2c_dev(class_dev); |
||
123 | return print_dev_t(buf, MKDEV(I2C_MAJOR, i2c_dev->minor)); |
||
124 | } |
||
125 | static CLASS_DEVICE_ATTR(dev, S_IRUGO, show_dev, NULL); |
||
126 | |||
127 | static ssize_t i2cdev_read (struct file *file, char __user *buf, size_t count, |
||
128 | loff_t *offset) |
||
129 | { |
||
130 | char *tmp; |
||
131 | int ret; |
||
132 | |||
133 | struct i2c_client *client = (struct i2c_client *)file->private_data; |
||
134 | |||
135 | if (count > 8192) |
||
136 | count = 8192; |
||
137 | |||
138 | tmp = kmalloc(count,GFP_KERNEL); |
||
139 | if (tmp==NULL) |
||
140 | return -ENOMEM; |
||
141 | |||
142 | pr_debug("i2c-dev.o: i2c-%d reading %d bytes.\n", |
||
496 | giacomo | 143 | (int)iminor(file->f_dentry->d_inode), (int)count); |
420 | giacomo | 144 | |
145 | ret = i2c_master_recv(client,tmp,count); |
||
146 | if (ret >= 0) |
||
147 | ret = copy_to_user(buf,tmp,count)?-EFAULT:ret; |
||
148 | kfree(tmp); |
||
149 | return ret; |
||
150 | } |
||
151 | |||
152 | static ssize_t i2cdev_write (struct file *file, const char __user *buf, size_t count, |
||
153 | loff_t *offset) |
||
154 | { |
||
155 | int ret; |
||
156 | char *tmp; |
||
157 | struct i2c_client *client = (struct i2c_client *)file->private_data; |
||
158 | |||
159 | if (count > 8192) |
||
160 | count = 8192; |
||
161 | |||
162 | tmp = kmalloc(count,GFP_KERNEL); |
||
163 | if (tmp==NULL) |
||
164 | return -ENOMEM; |
||
165 | if (copy_from_user(tmp,buf,count)) { |
||
166 | kfree(tmp); |
||
167 | return -EFAULT; |
||
168 | } |
||
169 | |||
170 | pr_debug("i2c-dev.o: i2c-%d writing %d bytes.\n", |
||
496 | giacomo | 171 | (int)iminor(file->f_dentry->d_inode), (int)count); |
420 | giacomo | 172 | |
173 | ret = i2c_master_send(client,tmp,count); |
||
174 | kfree(tmp); |
||
175 | return ret; |
||
176 | } |
||
177 | |||
178 | int i2cdev_ioctl (struct inode *inode, struct file *file, unsigned int cmd, |
||
179 | unsigned long arg) |
||
180 | { |
||
181 | struct i2c_client *client = (struct i2c_client *)file->private_data; |
||
182 | struct i2c_rdwr_ioctl_data rdwr_arg; |
||
183 | struct i2c_smbus_ioctl_data data_arg; |
||
184 | union i2c_smbus_data temp; |
||
185 | struct i2c_msg *rdwr_pa; |
||
186 | u8 **data_ptrs; |
||
187 | int i,datasize,res; |
||
188 | unsigned long funcs; |
||
189 | |||
190 | dev_dbg(&client->dev, "i2c-%d ioctl, cmd: 0x%x, arg: %lx.\n", |
||
191 | iminor(inode),cmd, arg); |
||
192 | |||
193 | switch ( cmd ) { |
||
194 | case I2C_SLAVE: |
||
195 | case I2C_SLAVE_FORCE: |
||
196 | if ((arg > 0x3ff) || |
||
197 | (((client->flags & I2C_M_TEN) == 0) && arg > 0x7f)) |
||
198 | return -EINVAL; |
||
199 | if ((cmd == I2C_SLAVE) && i2c_check_addr(client->adapter,arg)) |
||
200 | return -EBUSY; |
||
201 | client->addr = arg; |
||
202 | return 0; |
||
203 | case I2C_TENBIT: |
||
204 | if (arg) |
||
205 | client->flags |= I2C_M_TEN; |
||
206 | else |
||
207 | client->flags &= ~I2C_M_TEN; |
||
208 | return 0; |
||
209 | case I2C_PEC: |
||
210 | if (arg) |
||
211 | client->flags |= I2C_CLIENT_PEC; |
||
212 | else |
||
213 | client->flags &= ~I2C_CLIENT_PEC; |
||
214 | return 0; |
||
215 | case I2C_FUNCS: |
||
216 | funcs = i2c_get_functionality(client->adapter); |
||
217 | return (copy_to_user((unsigned long __user *)arg, &funcs, |
||
218 | sizeof(unsigned long)))?-EFAULT:0; |
||
219 | |||
220 | case I2C_RDWR: |
||
221 | if (copy_from_user(&rdwr_arg, |
||
222 | (struct i2c_rdwr_ioctl_data __user *)arg, |
||
223 | sizeof(rdwr_arg))) |
||
224 | return -EFAULT; |
||
225 | |||
226 | /* Put an arbritrary limit on the number of messages that can |
||
227 | * be sent at once */ |
||
228 | if (rdwr_arg.nmsgs > 42) |
||
229 | return -EINVAL; |
||
230 | |||
231 | rdwr_pa = (struct i2c_msg *) |
||
232 | kmalloc(rdwr_arg.nmsgs * sizeof(struct i2c_msg), |
||
233 | GFP_KERNEL); |
||
234 | |||
235 | if (rdwr_pa == NULL) return -ENOMEM; |
||
236 | |||
237 | if (copy_from_user(rdwr_pa, rdwr_arg.msgs, |
||
238 | rdwr_arg.nmsgs * sizeof(struct i2c_msg))) { |
||
239 | kfree(rdwr_pa); |
||
240 | return -EFAULT; |
||
241 | } |
||
242 | |||
243 | data_ptrs = (u8 **) kmalloc(rdwr_arg.nmsgs * sizeof(u8 *), |
||
244 | GFP_KERNEL); |
||
245 | if (data_ptrs == NULL) { |
||
246 | kfree(rdwr_pa); |
||
247 | return -ENOMEM; |
||
248 | } |
||
249 | |||
250 | res = 0; |
||
251 | for( i=0; i<rdwr_arg.nmsgs; i++ ) { |
||
252 | /* Limit the size of the message to a sane amount */ |
||
253 | if (rdwr_pa[i].len > 8192) { |
||
254 | res = -EINVAL; |
||
255 | break; |
||
256 | } |
||
257 | data_ptrs[i] = rdwr_pa[i].buf; |
||
258 | rdwr_pa[i].buf = kmalloc(rdwr_pa[i].len, GFP_KERNEL); |
||
259 | if(rdwr_pa[i].buf == NULL) { |
||
260 | res = -ENOMEM; |
||
261 | break; |
||
262 | } |
||
263 | if(copy_from_user(rdwr_pa[i].buf, |
||
264 | data_ptrs[i], |
||
265 | rdwr_pa[i].len)) { |
||
266 | ++i; /* Needs to be kfreed too */ |
||
267 | res = -EFAULT; |
||
268 | break; |
||
269 | } |
||
270 | } |
||
271 | if (res < 0) { |
||
272 | int j; |
||
273 | for (j = 0; j < i; ++j) |
||
274 | kfree(rdwr_pa[j].buf); |
||
275 | kfree(data_ptrs); |
||
276 | kfree(rdwr_pa); |
||
277 | return res; |
||
278 | } |
||
279 | |||
280 | res = i2c_transfer(client->adapter, |
||
281 | rdwr_pa, |
||
282 | rdwr_arg.nmsgs); |
||
283 | while(i-- > 0) { |
||
284 | if( res>=0 && (rdwr_pa[i].flags & I2C_M_RD)) { |
||
285 | if(copy_to_user( |
||
286 | data_ptrs[i], |
||
287 | rdwr_pa[i].buf, |
||
288 | rdwr_pa[i].len)) { |
||
289 | res = -EFAULT; |
||
290 | } |
||
291 | } |
||
292 | kfree(rdwr_pa[i].buf); |
||
293 | } |
||
294 | kfree(data_ptrs); |
||
295 | kfree(rdwr_pa); |
||
296 | return res; |
||
297 | |||
298 | case I2C_SMBUS: |
||
299 | if (copy_from_user(&data_arg, |
||
300 | (struct i2c_smbus_ioctl_data __user *) arg, |
||
301 | sizeof(struct i2c_smbus_ioctl_data))) |
||
302 | return -EFAULT; |
||
303 | if ((data_arg.size != I2C_SMBUS_BYTE) && |
||
304 | (data_arg.size != I2C_SMBUS_QUICK) && |
||
305 | (data_arg.size != I2C_SMBUS_BYTE_DATA) && |
||
306 | (data_arg.size != I2C_SMBUS_WORD_DATA) && |
||
307 | (data_arg.size != I2C_SMBUS_PROC_CALL) && |
||
308 | (data_arg.size != I2C_SMBUS_BLOCK_DATA) && |
||
309 | (data_arg.size != I2C_SMBUS_I2C_BLOCK_DATA) && |
||
310 | (data_arg.size != I2C_SMBUS_BLOCK_PROC_CALL)) { |
||
311 | dev_dbg(&client->dev, |
||
312 | "size out of range (%x) in ioctl I2C_SMBUS.\n", |
||
313 | data_arg.size); |
||
314 | return -EINVAL; |
||
315 | } |
||
316 | /* Note that I2C_SMBUS_READ and I2C_SMBUS_WRITE are 0 and 1, |
||
317 | so the check is valid if size==I2C_SMBUS_QUICK too. */ |
||
318 | if ((data_arg.read_write != I2C_SMBUS_READ) && |
||
319 | (data_arg.read_write != I2C_SMBUS_WRITE)) { |
||
320 | dev_dbg(&client->dev, |
||
321 | "read_write out of range (%x) in ioctl I2C_SMBUS.\n", |
||
322 | data_arg.read_write); |
||
323 | return -EINVAL; |
||
324 | } |
||
325 | |||
326 | /* Note that command values are always valid! */ |
||
327 | |||
328 | if ((data_arg.size == I2C_SMBUS_QUICK) || |
||
329 | ((data_arg.size == I2C_SMBUS_BYTE) && |
||
330 | (data_arg.read_write == I2C_SMBUS_WRITE))) |
||
331 | /* These are special: we do not use data */ |
||
332 | return i2c_smbus_xfer(client->adapter, client->addr, |
||
333 | client->flags, |
||
334 | data_arg.read_write, |
||
335 | data_arg.command, |
||
336 | data_arg.size, NULL); |
||
337 | |||
338 | if (data_arg.data == NULL) { |
||
339 | dev_dbg(&client->dev, |
||
340 | "data is NULL pointer in ioctl I2C_SMBUS.\n"); |
||
341 | return -EINVAL; |
||
342 | } |
||
343 | |||
344 | if ((data_arg.size == I2C_SMBUS_BYTE_DATA) || |
||
345 | (data_arg.size == I2C_SMBUS_BYTE)) |
||
346 | datasize = sizeof(data_arg.data->byte); |
||
347 | else if ((data_arg.size == I2C_SMBUS_WORD_DATA) || |
||
348 | (data_arg.size == I2C_SMBUS_PROC_CALL)) |
||
349 | datasize = sizeof(data_arg.data->word); |
||
350 | else /* size == smbus block, i2c block, or block proc. call */ |
||
351 | datasize = sizeof(data_arg.data->block); |
||
352 | |||
353 | if ((data_arg.size == I2C_SMBUS_PROC_CALL) || |
||
354 | (data_arg.size == I2C_SMBUS_BLOCK_PROC_CALL) || |
||
355 | (data_arg.read_write == I2C_SMBUS_WRITE)) { |
||
356 | if (copy_from_user(&temp, data_arg.data, datasize)) |
||
357 | return -EFAULT; |
||
358 | } |
||
359 | res = i2c_smbus_xfer(client->adapter,client->addr,client->flags, |
||
360 | data_arg.read_write, |
||
361 | data_arg.command,data_arg.size,&temp); |
||
362 | if (! res && ((data_arg.size == I2C_SMBUS_PROC_CALL) || |
||
363 | (data_arg.size == I2C_SMBUS_BLOCK_PROC_CALL) || |
||
364 | (data_arg.read_write == I2C_SMBUS_READ))) { |
||
365 | if (copy_to_user(data_arg.data, &temp, datasize)) |
||
366 | return -EFAULT; |
||
367 | } |
||
368 | return res; |
||
369 | |||
370 | default: |
||
371 | return i2c_control(client,cmd,arg); |
||
372 | } |
||
373 | return 0; |
||
374 | } |
||
375 | |||
376 | static int i2cdev_open(struct inode *inode, struct file *file) |
||
377 | { |
||
378 | unsigned int minor = iminor(inode); |
||
379 | struct i2c_client *client; |
||
380 | struct i2c_adapter *adap; |
||
381 | struct i2c_dev *i2c_dev; |
||
382 | |||
383 | i2c_dev = i2c_dev_get_by_minor(minor); |
||
384 | if (!i2c_dev) |
||
385 | return -ENODEV; |
||
386 | |||
387 | adap = i2c_get_adapter(i2c_dev->adap->nr); |
||
388 | if (!adap) |
||
389 | return -ENODEV; |
||
390 | |||
391 | client = kmalloc(sizeof(*client), GFP_KERNEL); |
||
392 | if (!client) { |
||
393 | i2c_put_adapter(adap); |
||
394 | return -ENOMEM; |
||
395 | } |
||
396 | memcpy(client, &i2cdev_client_template, sizeof(*client)); |
||
397 | |||
398 | /* registered with adapter, passed as client to user */ |
||
399 | client->adapter = adap; |
||
400 | file->private_data = client; |
||
401 | |||
402 | return 0; |
||
403 | } |
||
404 | |||
405 | static int i2cdev_release(struct inode *inode, struct file *file) |
||
406 | { |
||
407 | struct i2c_client *client = file->private_data; |
||
408 | |||
409 | i2c_put_adapter(client->adapter); |
||
410 | kfree(client); |
||
411 | file->private_data = NULL; |
||
412 | |||
413 | return 0; |
||
414 | } |
||
415 | |||
416 | static struct file_operations i2cdev_fops = { |
||
417 | .owner = THIS_MODULE, |
||
462 | giacomo | 418 | .llseek = NULL,//no_llseek, |
420 | giacomo | 419 | .read = i2cdev_read, |
420 | .write = i2cdev_write, |
||
421 | .ioctl = i2cdev_ioctl, |
||
422 | .open = i2cdev_open, |
||
423 | .release = i2cdev_release, |
||
424 | }; |
||
425 | |||
426 | static void release_i2c_dev(struct class_device *dev) |
||
427 | { |
||
428 | struct i2c_dev *i2c_dev = to_i2c_dev(dev); |
||
429 | complete(&i2c_dev->released); |
||
430 | } |
||
431 | |||
432 | static struct class i2c_dev_class = { |
||
433 | .name = "i2c-dev", |
||
434 | .release = &release_i2c_dev, |
||
435 | }; |
||
436 | |||
437 | static int i2cdev_attach_adapter(struct i2c_adapter *adap) |
||
438 | { |
||
439 | struct i2c_dev *i2c_dev; |
||
440 | int retval; |
||
441 | |||
442 | i2c_dev = get_free_i2c_dev(); |
||
443 | if (IS_ERR(i2c_dev)) |
||
444 | return PTR_ERR(i2c_dev); |
||
445 | |||
446 | devfs_mk_cdev(MKDEV(I2C_MAJOR, i2c_dev->minor), |
||
447 | S_IFCHR|S_IRUSR|S_IWUSR, "i2c/%d", i2c_dev->minor); |
||
448 | dev_dbg(&adap->dev, "Registered as minor %d\n", i2c_dev->minor); |
||
449 | |||
450 | /* register this i2c device with the driver core */ |
||
451 | i2c_dev->adap = adap; |
||
452 | if (adap->dev.parent == &legacy_bus) |
||
453 | i2c_dev->class_dev.dev = &adap->dev; |
||
454 | else |
||
455 | i2c_dev->class_dev.dev = adap->dev.parent; |
||
456 | i2c_dev->class_dev.class = &i2c_dev_class; |
||
458 | giacomo | 457 | snprintf26(i2c_dev->class_dev.class_id, BUS_ID_SIZE, "i2c-%d", i2c_dev->minor); |
420 | giacomo | 458 | retval = class_device_register(&i2c_dev->class_dev); |
459 | if (retval) |
||
460 | goto error; |
||
461 | class_device_create_file(&i2c_dev->class_dev, &class_device_attr_dev); |
||
462 | return 0; |
||
463 | error: |
||
464 | return_i2c_dev(i2c_dev); |
||
465 | kfree(i2c_dev); |
||
466 | return retval; |
||
467 | } |
||
468 | |||
469 | static int i2cdev_detach_adapter(struct i2c_adapter *adap) |
||
470 | { |
||
471 | struct i2c_dev *i2c_dev; |
||
472 | |||
473 | i2c_dev = i2c_dev_get_by_adapter(adap); |
||
474 | if (!i2c_dev) |
||
475 | return -ENODEV; |
||
476 | |||
477 | init_completion(&i2c_dev->released); |
||
478 | devfs_remove("i2c/%d", i2c_dev->minor); |
||
479 | return_i2c_dev(i2c_dev); |
||
480 | class_device_unregister(&i2c_dev->class_dev); |
||
481 | wait_for_completion(&i2c_dev->released); |
||
482 | kfree(i2c_dev); |
||
483 | |||
484 | dev_dbg(&adap->dev, "Adapter unregistered\n"); |
||
485 | return 0; |
||
486 | } |
||
487 | |||
488 | static int i2cdev_detach_client(struct i2c_client *client) |
||
489 | { |
||
490 | return 0; |
||
491 | } |
||
492 | |||
493 | static int i2cdev_command(struct i2c_client *client, unsigned int cmd, |
||
494 | void *arg) |
||
495 | { |
||
496 | return -1; |
||
497 | } |
||
498 | |||
499 | static struct i2c_driver i2cdev_driver = { |
||
500 | .owner = THIS_MODULE, |
||
501 | .name = "dev_driver", |
||
502 | .id = I2C_DRIVERID_I2CDEV, |
||
503 | .flags = I2C_DF_NOTIFY, |
||
504 | .attach_adapter = i2cdev_attach_adapter, |
||
505 | .detach_adapter = i2cdev_detach_adapter, |
||
506 | .detach_client = i2cdev_detach_client, |
||
507 | .command = i2cdev_command, |
||
508 | }; |
||
509 | |||
510 | static struct i2c_client i2cdev_client_template = { |
||
511 | .name = "I2C /dev entry", |
||
512 | .id = 1, |
||
513 | .addr = -1, |
||
514 | .driver = &i2cdev_driver, |
||
515 | }; |
||
516 | |||
458 | giacomo | 517 | int __init i2c_dev_init(void) |
420 | giacomo | 518 | { |
519 | int res; |
||
520 | |||
521 | printk(KERN_INFO "i2c /dev entries driver\n"); |
||
522 | |||
523 | if (register_chrdev(I2C_MAJOR,"i2c",&i2cdev_fops)) { |
||
524 | printk(KERN_ERR "i2c-dev.o: unable to get major %d for i2c bus\n", |
||
525 | I2C_MAJOR); |
||
526 | return -EIO; |
||
527 | } |
||
528 | devfs_mk_dir("i2c"); |
||
529 | class_register(&i2c_dev_class); |
||
530 | if ((res = i2c_add_driver(&i2cdev_driver))) { |
||
531 | printk(KERN_ERR "i2c-dev.o: Driver registration failed, module not inserted.\n"); |
||
532 | devfs_remove("i2c"); |
||
533 | unregister_chrdev(I2C_MAJOR,"i2c"); |
||
534 | return res; |
||
535 | } |
||
536 | return 0; |
||
537 | } |
||
538 | |||
539 | static void __exit i2c_dev_exit(void) |
||
540 | { |
||
541 | i2c_del_driver(&i2cdev_driver); |
||
542 | class_unregister(&i2c_dev_class); |
||
543 | devfs_remove("i2c"); |
||
544 | unregister_chrdev(I2C_MAJOR,"i2c"); |
||
545 | } |
||
546 | |||
547 | MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl> and " |
||
548 | "Simon G. Vogl <simon@tk.uni-linz.ac.at>"); |
||
549 | MODULE_DESCRIPTION("I2C /dev entries driver"); |
||
550 | MODULE_LICENSE("GPL"); |
||
551 | |||
552 | module_init(i2c_dev_init); |
||
553 | module_exit(i2c_dev_exit); |