blob: 7bf7485377e60e471442374edb3fcfd001cb62b5 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * The DSP56001 Device Driver, saviour of the Free World(tm)
3 *
4 * Authors: Fredrik Noring <noring@nocrew.org>
5 * lars brinkhoff <lars@nocrew.org>
6 * Tomas Berndtsson <tomas@nocrew.org>
7 *
8 * First version May 1996
9 *
10 * History:
11 * 97-01-29 Tomas Berndtsson,
12 * Integrated with Linux 2.1.21 kernel sources.
13 * 97-02-15 Tomas Berndtsson,
14 * Fixed for kernel 2.1.26
15 *
16 * BUGS:
17 * Hmm... there must be something here :)
18 *
19 * Copyright (C) 1996,1997 Fredrik Noring, lars brinkhoff & Tomas Berndtsson
20 *
21 * This file is subject to the terms and conditions of the GNU General Public
22 * License. See the file COPYING in the main directory of this archive
23 * for more details.
24 */
25
26#include <linux/module.h>
27#include <linux/slab.h> /* for kmalloc() and kfree() */
Linus Torvalds1da177e2005-04-16 15:20:36 -070028#include <linux/major.h>
29#include <linux/types.h>
30#include <linux/errno.h>
31#include <linux/delay.h> /* guess what */
32#include <linux/fs.h>
33#include <linux/mm.h>
34#include <linux/init.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070035#include <linux/device.h>
Jonathan Corbet65f37b72008-05-16 13:57:31 -060036#include <linux/smp_lock.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070037
38#include <asm/atarihw.h>
39#include <asm/traps.h>
40#include <asm/uaccess.h> /* For put_user and get_user */
41
42#include <asm/dsp56k.h>
43
44/* minor devices */
45#define DSP56K_DEV_56001 0 /* The only device so far */
46
47#define TIMEOUT 10 /* Host port timeout in number of tries */
48#define MAXIO 2048 /* Maximum number of words before sleep */
49#define DSP56K_MAX_BINARY_LENGTH (3*64*1024)
50
51#define DSP56K_TX_INT_ON dsp56k_host_interface.icr |= DSP56K_ICR_TREQ
52#define DSP56K_RX_INT_ON dsp56k_host_interface.icr |= DSP56K_ICR_RREQ
53#define DSP56K_TX_INT_OFF dsp56k_host_interface.icr &= ~DSP56K_ICR_TREQ
54#define DSP56K_RX_INT_OFF dsp56k_host_interface.icr &= ~DSP56K_ICR_RREQ
55
56#define DSP56K_TRANSMIT (dsp56k_host_interface.isr & DSP56K_ISR_TXDE)
57#define DSP56K_RECEIVE (dsp56k_host_interface.isr & DSP56K_ISR_RXDF)
58
59#define handshake(count, maxio, timeout, ENABLE, f) \
60{ \
61 long i, t, m; \
62 while (count > 0) { \
63 m = min_t(unsigned long, count, maxio); \
64 for (i = 0; i < m; i++) { \
65 for (t = 0; t < timeout && !ENABLE; t++) \
66 msleep(20); \
67 if(!ENABLE) \
68 return -EIO; \
69 f; \
70 } \
71 count -= m; \
72 if (m == maxio) msleep(20); \
73 } \
74}
75
76#define tx_wait(n) \
77{ \
78 int t; \
79 for(t = 0; t < n && !DSP56K_TRANSMIT; t++) \
80 msleep(10); \
81 if(!DSP56K_TRANSMIT) { \
82 return -EIO; \
83 } \
84}
85
86#define rx_wait(n) \
87{ \
88 int t; \
89 for(t = 0; t < n && !DSP56K_RECEIVE; t++) \
90 msleep(10); \
91 if(!DSP56K_RECEIVE) { \
92 return -EIO; \
93 } \
94}
95
96/* DSP56001 bootstrap code */
97static char bootstrap[] = {
98 0x0c, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
99 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
100 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
101 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
102 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
103 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
104 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
105 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
106 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
107 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
108 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
109 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
110 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
111 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
112 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
113 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
114 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
115 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
116 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
117 0x00, 0x00, 0x60, 0xf4, 0x00, 0x00, 0x00, 0x4f, 0x61, 0xf4,
118 0x00, 0x00, 0x7e, 0xa9, 0x06, 0x2e, 0x80, 0x00, 0x00, 0x47,
119 0x07, 0xd8, 0x84, 0x07, 0x59, 0x84, 0x08, 0xf4, 0xa8, 0x00,
120 0x00, 0x04, 0x08, 0xf4, 0xbf, 0x00, 0x0c, 0x00, 0x00, 0xfe,
121 0xb8, 0x0a, 0xf0, 0x80, 0x00, 0x7e, 0xa9, 0x08, 0xf4, 0xa0,
122 0x00, 0x00, 0x01, 0x08, 0xf4, 0xbe, 0x00, 0x00, 0x00, 0x0a,
123 0xa9, 0x80, 0x00, 0x7e, 0xad, 0x08, 0x4e, 0x2b, 0x44, 0xf4,
124 0x00, 0x00, 0x00, 0x03, 0x44, 0xf4, 0x45, 0x00, 0x00, 0x01,
125 0x0e, 0xa0, 0x00, 0x0a, 0xa9, 0x80, 0x00, 0x7e, 0xb5, 0x08,
126 0x50, 0x2b, 0x0a, 0xa9, 0x80, 0x00, 0x7e, 0xb8, 0x08, 0x46,
127 0x2b, 0x44, 0xf4, 0x45, 0x00, 0x00, 0x02, 0x0a, 0xf0, 0xaa,
128 0x00, 0x7e, 0xc9, 0x20, 0x00, 0x45, 0x0a, 0xf0, 0xaa, 0x00,
129 0x7e, 0xd0, 0x06, 0xc6, 0x00, 0x00, 0x7e, 0xc6, 0x0a, 0xa9,
130 0x80, 0x00, 0x7e, 0xc4, 0x08, 0x58, 0x6b, 0x0a, 0xf0, 0x80,
131 0x00, 0x7e, 0xad, 0x06, 0xc6, 0x00, 0x00, 0x7e, 0xcd, 0x0a,
132 0xa9, 0x80, 0x00, 0x7e, 0xcb, 0x08, 0x58, 0xab, 0x0a, 0xf0,
133 0x80, 0x00, 0x7e, 0xad, 0x06, 0xc6, 0x00, 0x00, 0x7e, 0xd4,
134 0x0a, 0xa9, 0x80, 0x00, 0x7e, 0xd2, 0x08, 0x58, 0xeb, 0x0a,
135 0xf0, 0x80, 0x00, 0x7e, 0xad};
136static int sizeof_bootstrap = 375;
137
138
139static struct dsp56k_device {
Al Viro64b33612007-10-14 19:35:20 +0100140 unsigned long in_use;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700141 long maxio, timeout;
142 int tx_wsize, rx_wsize;
143} dsp56k;
144
gregkh@suse.deca8eca62005-03-23 09:53:09 -0800145static struct class *dsp56k_class;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700146
147static int dsp56k_reset(void)
148{
149 u_char status;
150
151 /* Power down the DSP */
152 sound_ym.rd_data_reg_sel = 14;
153 status = sound_ym.rd_data_reg_sel & 0xef;
154 sound_ym.wd_data = status;
155 sound_ym.wd_data = status | 0x10;
156
157 udelay(10);
158
159 /* Power up the DSP */
160 sound_ym.rd_data_reg_sel = 14;
161 sound_ym.wd_data = sound_ym.rd_data_reg_sel & 0xef;
162
163 return 0;
164}
165
Al Virod85f6892006-01-12 01:06:31 -0800166static int dsp56k_upload(u_char __user *bin, int len)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700167{
168 int i;
169 u_char *p;
170
171 dsp56k_reset();
172
173 p = bootstrap;
174 for (i = 0; i < sizeof_bootstrap/3; i++) {
175 /* tx_wait(10); */
176 dsp56k_host_interface.data.b[1] = *p++;
177 dsp56k_host_interface.data.b[2] = *p++;
178 dsp56k_host_interface.data.b[3] = *p++;
179 }
180 for (; i < 512; i++) {
181 /* tx_wait(10); */
182 dsp56k_host_interface.data.b[1] = 0;
183 dsp56k_host_interface.data.b[2] = 0;
184 dsp56k_host_interface.data.b[3] = 0;
185 }
186
187 for (i = 0; i < len; i++) {
188 tx_wait(10);
189 get_user(dsp56k_host_interface.data.b[1], bin++);
190 get_user(dsp56k_host_interface.data.b[2], bin++);
191 get_user(dsp56k_host_interface.data.b[3], bin++);
192 }
193
194 tx_wait(10);
195 dsp56k_host_interface.data.l = 3; /* Magic execute */
196
197 return 0;
198}
199
Al Virod85f6892006-01-12 01:06:31 -0800200static ssize_t dsp56k_read(struct file *file, char __user *buf, size_t count,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700201 loff_t *ppos)
202{
Josef Sipeka7113a92006-12-08 02:36:55 -0800203 struct inode *inode = file->f_path.dentry->d_inode;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700204 int dev = iminor(inode) & 0x0f;
205
206 switch(dev)
207 {
208 case DSP56K_DEV_56001:
209 {
210
211 long n;
212
213 /* Don't do anything if nothing is to be done */
214 if (!count) return 0;
215
216 n = 0;
217 switch (dsp56k.rx_wsize) {
218 case 1: /* 8 bit */
219 {
220 handshake(count, dsp56k.maxio, dsp56k.timeout, DSP56K_RECEIVE,
221 put_user(dsp56k_host_interface.data.b[3], buf+n++));
222 return n;
223 }
224 case 2: /* 16 bit */
225 {
Al Virod85f6892006-01-12 01:06:31 -0800226 short __user *data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700227
228 count /= 2;
Al Virod85f6892006-01-12 01:06:31 -0800229 data = (short __user *) buf;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700230 handshake(count, dsp56k.maxio, dsp56k.timeout, DSP56K_RECEIVE,
231 put_user(dsp56k_host_interface.data.w[1], data+n++));
232 return 2*n;
233 }
234 case 3: /* 24 bit */
235 {
236 count /= 3;
237 handshake(count, dsp56k.maxio, dsp56k.timeout, DSP56K_RECEIVE,
238 put_user(dsp56k_host_interface.data.b[1], buf+n++);
239 put_user(dsp56k_host_interface.data.b[2], buf+n++);
240 put_user(dsp56k_host_interface.data.b[3], buf+n++));
241 return 3*n;
242 }
243 case 4: /* 32 bit */
244 {
Al Virod85f6892006-01-12 01:06:31 -0800245 long __user *data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700246
247 count /= 4;
Al Virod85f6892006-01-12 01:06:31 -0800248 data = (long __user *) buf;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700249 handshake(count, dsp56k.maxio, dsp56k.timeout, DSP56K_RECEIVE,
250 put_user(dsp56k_host_interface.data.l, data+n++));
251 return 4*n;
252 }
253 }
254 return -EFAULT;
255 }
256
257 default:
258 printk(KERN_ERR "DSP56k driver: Unknown minor device: %d\n", dev);
259 return -ENXIO;
260 }
261}
262
Al Virod85f6892006-01-12 01:06:31 -0800263static ssize_t dsp56k_write(struct file *file, const char __user *buf, size_t count,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700264 loff_t *ppos)
265{
Josef Sipeka7113a92006-12-08 02:36:55 -0800266 struct inode *inode = file->f_path.dentry->d_inode;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700267 int dev = iminor(inode) & 0x0f;
268
269 switch(dev)
270 {
271 case DSP56K_DEV_56001:
272 {
273 long n;
274
275 /* Don't do anything if nothing is to be done */
276 if (!count) return 0;
277
278 n = 0;
279 switch (dsp56k.tx_wsize) {
280 case 1: /* 8 bit */
281 {
282 handshake(count, dsp56k.maxio, dsp56k.timeout, DSP56K_TRANSMIT,
283 get_user(dsp56k_host_interface.data.b[3], buf+n++));
284 return n;
285 }
286 case 2: /* 16 bit */
287 {
Al Virod85f6892006-01-12 01:06:31 -0800288 const short __user *data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700289
290 count /= 2;
Al Virod85f6892006-01-12 01:06:31 -0800291 data = (const short __user *)buf;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700292 handshake(count, dsp56k.maxio, dsp56k.timeout, DSP56K_TRANSMIT,
293 get_user(dsp56k_host_interface.data.w[1], data+n++));
294 return 2*n;
295 }
296 case 3: /* 24 bit */
297 {
298 count /= 3;
299 handshake(count, dsp56k.maxio, dsp56k.timeout, DSP56K_TRANSMIT,
300 get_user(dsp56k_host_interface.data.b[1], buf+n++);
301 get_user(dsp56k_host_interface.data.b[2], buf+n++);
302 get_user(dsp56k_host_interface.data.b[3], buf+n++));
303 return 3*n;
304 }
305 case 4: /* 32 bit */
306 {
Al Virod85f6892006-01-12 01:06:31 -0800307 const long __user *data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700308
309 count /= 4;
Al Virod85f6892006-01-12 01:06:31 -0800310 data = (const long __user *)buf;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700311 handshake(count, dsp56k.maxio, dsp56k.timeout, DSP56K_TRANSMIT,
312 get_user(dsp56k_host_interface.data.l, data+n++));
313 return 4*n;
314 }
315 }
316
317 return -EFAULT;
318 }
319 default:
320 printk(KERN_ERR "DSP56k driver: Unknown minor device: %d\n", dev);
321 return -ENXIO;
322 }
323}
324
325static int dsp56k_ioctl(struct inode *inode, struct file *file,
326 unsigned int cmd, unsigned long arg)
327{
328 int dev = iminor(inode) & 0x0f;
Al Virod85f6892006-01-12 01:06:31 -0800329 void __user *argp = (void __user *)arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700330
331 switch(dev)
332 {
333 case DSP56K_DEV_56001:
334
335 switch(cmd) {
336 case DSP56K_UPLOAD:
337 {
Al Virod85f6892006-01-12 01:06:31 -0800338 char __user *bin;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700339 int r, len;
Al Virod85f6892006-01-12 01:06:31 -0800340 struct dsp56k_upload __user *binary = argp;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700341
342 if(get_user(len, &binary->len) < 0)
343 return -EFAULT;
344 if(get_user(bin, &binary->bin) < 0)
345 return -EFAULT;
346
347 if (len == 0) {
348 return -EINVAL; /* nothing to upload?!? */
349 }
350 if (len > DSP56K_MAX_BINARY_LENGTH) {
351 return -EINVAL;
352 }
353
354 r = dsp56k_upload(bin, len);
355 if (r < 0) {
356 return r;
357 }
358
359 break;
360 }
361 case DSP56K_SET_TX_WSIZE:
362 if (arg > 4 || arg < 1)
363 return -EINVAL;
364 dsp56k.tx_wsize = (int) arg;
365 break;
366 case DSP56K_SET_RX_WSIZE:
367 if (arg > 4 || arg < 1)
368 return -EINVAL;
369 dsp56k.rx_wsize = (int) arg;
370 break;
371 case DSP56K_HOST_FLAGS:
372 {
373 int dir, out, status;
Al Virod85f6892006-01-12 01:06:31 -0800374 struct dsp56k_host_flags __user *hf = argp;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700375
376 if(get_user(dir, &hf->dir) < 0)
377 return -EFAULT;
378 if(get_user(out, &hf->out) < 0)
379 return -EFAULT;
380
381 if ((dir & 0x1) && (out & 0x1))
382 dsp56k_host_interface.icr |= DSP56K_ICR_HF0;
383 else if (dir & 0x1)
384 dsp56k_host_interface.icr &= ~DSP56K_ICR_HF0;
385 if ((dir & 0x2) && (out & 0x2))
386 dsp56k_host_interface.icr |= DSP56K_ICR_HF1;
387 else if (dir & 0x2)
388 dsp56k_host_interface.icr &= ~DSP56K_ICR_HF1;
389
390 status = 0;
391 if (dsp56k_host_interface.icr & DSP56K_ICR_HF0) status |= 0x1;
392 if (dsp56k_host_interface.icr & DSP56K_ICR_HF1) status |= 0x2;
393 if (dsp56k_host_interface.isr & DSP56K_ISR_HF2) status |= 0x4;
394 if (dsp56k_host_interface.isr & DSP56K_ISR_HF3) status |= 0x8;
395
396 return put_user(status, &hf->status);
397 }
398 case DSP56K_HOST_CMD:
399 if (arg > 31 || arg < 0)
400 return -EINVAL;
401 dsp56k_host_interface.cvr = (u_char)((arg & DSP56K_CVR_HV_MASK) |
402 DSP56K_CVR_HC);
403 break;
404 default:
405 return -EINVAL;
406 }
407 return 0;
408
409 default:
410 printk(KERN_ERR "DSP56k driver: Unknown minor device: %d\n", dev);
411 return -ENXIO;
412 }
413}
414
415/* As of 2.1.26 this should be dsp56k_poll,
416 * but how do I then check device minor number?
417 * Do I need this function at all???
418 */
419#if 0
420static unsigned int dsp56k_poll(struct file *file, poll_table *wait)
421{
Josef Sipeka7113a92006-12-08 02:36:55 -0800422 int dev = iminor(file->f_path.dentry->d_inode) & 0x0f;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700423
424 switch(dev)
425 {
426 case DSP56K_DEV_56001:
427 /* poll_wait(file, ???, wait); */
428 return POLLIN | POLLRDNORM | POLLOUT;
429
430 default:
431 printk("DSP56k driver: Unknown minor device: %d\n", dev);
432 return 0;
433 }
434}
435#endif
436
437static int dsp56k_open(struct inode *inode, struct file *file)
438{
439 int dev = iminor(inode) & 0x0f;
Jonathan Corbet65f37b72008-05-16 13:57:31 -0600440 int ret = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700441
Jonathan Corbet65f37b72008-05-16 13:57:31 -0600442 lock_kernel();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700443 switch(dev)
444 {
445 case DSP56K_DEV_56001:
446
Jonathan Corbet65f37b72008-05-16 13:57:31 -0600447 if (test_and_set_bit(0, &dsp56k.in_use)) {
448 ret = -EBUSY;
449 goto out;
450 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700451
452 dsp56k.timeout = TIMEOUT;
453 dsp56k.maxio = MAXIO;
454 dsp56k.rx_wsize = dsp56k.tx_wsize = 4;
455
456 DSP56K_TX_INT_OFF;
457 DSP56K_RX_INT_OFF;
458
459 /* Zero host flags */
460 dsp56k_host_interface.icr &= ~DSP56K_ICR_HF0;
461 dsp56k_host_interface.icr &= ~DSP56K_ICR_HF1;
462
463 break;
464
465 default:
Jonathan Corbet65f37b72008-05-16 13:57:31 -0600466 ret = -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700467 }
Jonathan Corbet65f37b72008-05-16 13:57:31 -0600468out:
469 unlock_kernel();
470 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700471}
472
473static int dsp56k_release(struct inode *inode, struct file *file)
474{
475 int dev = iminor(inode) & 0x0f;
476
477 switch(dev)
478 {
479 case DSP56K_DEV_56001:
480 clear_bit(0, &dsp56k.in_use);
481 break;
482 default:
483 printk(KERN_ERR "DSP56k driver: Unknown minor device: %d\n", dev);
484 return -ENXIO;
485 }
486
487 return 0;
488}
489
Arjan van de Ven62322d22006-07-03 00:24:21 -0700490static const struct file_operations dsp56k_fops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700491 .owner = THIS_MODULE,
492 .read = dsp56k_read,
493 .write = dsp56k_write,
494 .ioctl = dsp56k_ioctl,
495 .open = dsp56k_open,
496 .release = dsp56k_release,
497};
498
499
500/****** Init and module functions ******/
501
502static char banner[] __initdata = KERN_INFO "DSP56k driver installed\n";
503
504static int __init dsp56k_init_driver(void)
505{
506 int err = 0;
507
508 if(!MACH_IS_ATARI || !ATARIHW_PRESENT(DSP56K)) {
509 printk("DSP56k driver: Hardware not present\n");
510 return -ENODEV;
511 }
512
513 if(register_chrdev(DSP56K_MAJOR, "dsp56k", &dsp56k_fops)) {
514 printk("DSP56k driver: Unable to register driver\n");
515 return -ENODEV;
516 }
gregkh@suse.deca8eca62005-03-23 09:53:09 -0800517 dsp56k_class = class_create(THIS_MODULE, "dsp56k");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700518 if (IS_ERR(dsp56k_class)) {
519 err = PTR_ERR(dsp56k_class);
520 goto out_chrdev;
521 }
tonyj@suse.de07c015e2007-08-07 22:28:44 -0700522 device_create(dsp56k_class, NULL, MKDEV(DSP56K_MAJOR, 0), "dsp56k");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700523
Linus Torvalds1da177e2005-04-16 15:20:36 -0700524 printk(banner);
525 goto out;
526
Linus Torvalds1da177e2005-04-16 15:20:36 -0700527out_chrdev:
528 unregister_chrdev(DSP56K_MAJOR, "dsp56k");
529out:
530 return err;
531}
532module_init(dsp56k_init_driver);
533
534static void __exit dsp56k_cleanup_driver(void)
535{
tonyj@suse.de07c015e2007-08-07 22:28:44 -0700536 device_destroy(dsp56k_class, MKDEV(DSP56K_MAJOR, 0));
gregkh@suse.deca8eca62005-03-23 09:53:09 -0800537 class_destroy(dsp56k_class);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700538 unregister_chrdev(DSP56K_MAJOR, "dsp56k");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700539}
540module_exit(dsp56k_cleanup_driver);
541
542MODULE_LICENSE("GPL");