]> git.proxmox.com Git - mirror_ubuntu-artful-kernel.git/blame - drivers/media/video/gspca/pac207.c
V4L/DVB (12616): gspca_pac207: remove a number of unneeded (repeated) register writes
[mirror_ubuntu-artful-kernel.git] / drivers / media / video / gspca / pac207.c
CommitLineData
e2997a72
HG
1/*
2 * Pixart PAC207BCA library
3 *
dc1fe157 4 * Copyright (C) 2008 Hans de Goede <hdgoede@redhat.com>
e2997a72
HG
5 * Copyright (C) 2005 Thomas Kaiser thomas@kaiser-linux.li
6 * Copyleft (C) 2005 Michel Xhaard mxhaard@magic.fr
7 *
8 * V4L2 by Jean-Francois Moine <http://moinejf.free.fr>
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 *
24 */
25
26#define MODULE_NAME "pac207"
27
28#include "gspca.h"
29
dc1fe157 30MODULE_AUTHOR("Hans de Goede <hdgoede@redhat.com>");
e2997a72
HG
31MODULE_DESCRIPTION("Pixart PAC207");
32MODULE_LICENSE("GPL");
33
34#define PAC207_CTRL_TIMEOUT 100 /* ms */
35
36#define PAC207_BRIGHTNESS_MIN 0
37#define PAC207_BRIGHTNESS_MAX 255
38#define PAC207_BRIGHTNESS_DEFAULT 4 /* power on default: 4 */
39
46ccdafa
HG
40/* An exposure value of 4 also works (3 does not) but then we need to lower
41 the compression balance setting when in 352x288 mode, otherwise the usb
42 bandwidth is not enough and packets get dropped resulting in corrupt
43 frames. The problem with this is that when the compression balance gets
44 lowered below 0x80, the pac207 starts using a different compression
45 algorithm for some lines, these lines get prefixed with a 0x2dd2 prefix
46 and currently we do not know how to decompress these lines, so for now
47 we use a minimum exposure value of 5 */
48#define PAC207_EXPOSURE_MIN 5
e2997a72 49#define PAC207_EXPOSURE_MAX 26
46ccdafa 50#define PAC207_EXPOSURE_DEFAULT 5 /* power on default: 3 ?? */
e2997a72
HG
51#define PAC207_EXPOSURE_KNEE 11 /* 4 = 30 fps, 11 = 8, 15 = 6 */
52
53#define PAC207_GAIN_MIN 0
54#define PAC207_GAIN_MAX 31
55#define PAC207_GAIN_DEFAULT 9 /* power on default: 9 */
56#define PAC207_GAIN_KNEE 20
57
58#define PAC207_AUTOGAIN_DEADZONE 30
e2997a72 59
e2997a72
HG
60/* specific webcam descriptor */
61struct sd {
62 struct gspca_dev gspca_dev; /* !! must be the first item */
63
e2997a72
HG
64 u8 mode;
65
66 u8 brightness;
67 u8 exposure;
68 u8 autogain;
69 u8 gain;
70
71 u8 sof_read;
ab8f12cf 72 u8 header_read;
e2997a72
HG
73 u8 autogain_ignore_frames;
74
75 atomic_t avg_lum;
76};
77
78/* V4L2 controls supported by the driver */
79static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val);
80static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val);
81static int sd_setexposure(struct gspca_dev *gspca_dev, __s32 val);
82static int sd_getexposure(struct gspca_dev *gspca_dev, __s32 *val);
83static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val);
84static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val);
85static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val);
86static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val);
87
88static struct ctrl sd_ctrls[] = {
89#define SD_BRIGHTNESS 0
90 {
91 {
92 .id = V4L2_CID_BRIGHTNESS,
93 .type = V4L2_CTRL_TYPE_INTEGER,
94 .name = "Brightness",
95 .minimum = PAC207_BRIGHTNESS_MIN,
96 .maximum = PAC207_BRIGHTNESS_MAX,
97 .step = 1,
98 .default_value = PAC207_BRIGHTNESS_DEFAULT,
99 .flags = 0,
100 },
101 .set = sd_setbrightness,
102 .get = sd_getbrightness,
103 },
104#define SD_EXPOSURE 1
105 {
106 {
107 .id = V4L2_CID_EXPOSURE,
108 .type = V4L2_CTRL_TYPE_INTEGER,
109 .name = "exposure",
110 .minimum = PAC207_EXPOSURE_MIN,
111 .maximum = PAC207_EXPOSURE_MAX,
112 .step = 1,
113 .default_value = PAC207_EXPOSURE_DEFAULT,
114 .flags = 0,
115 },
116 .set = sd_setexposure,
117 .get = sd_getexposure,
118 },
119#define SD_AUTOGAIN 2
120 {
121 {
122 .id = V4L2_CID_AUTOGAIN,
123 .type = V4L2_CTRL_TYPE_BOOLEAN,
124 .name = "Auto Gain",
125 .minimum = 0,
126 .maximum = 1,
127 .step = 1,
66e4124f
JFM
128#define AUTOGAIN_DEF 1
129 .default_value = AUTOGAIN_DEF,
e2997a72
HG
130 .flags = 0,
131 },
132 .set = sd_setautogain,
133 .get = sd_getautogain,
134 },
135#define SD_GAIN 3
136 {
137 {
138 .id = V4L2_CID_GAIN,
139 .type = V4L2_CTRL_TYPE_INTEGER,
140 .name = "gain",
141 .minimum = PAC207_GAIN_MIN,
142 .maximum = PAC207_GAIN_MAX,
143 .step = 1,
144 .default_value = PAC207_GAIN_DEFAULT,
145 .flags = 0,
146 },
147 .set = sd_setgain,
148 .get = sd_getgain,
149 },
150};
151
cc611b8a 152static const struct v4l2_pix_format sif_mode[] = {
c2446b3e
JFM
153 {176, 144, V4L2_PIX_FMT_PAC207, V4L2_FIELD_NONE,
154 .bytesperline = 176,
155 .sizeimage = (176 + 2) * 144,
156 /* uncompressed, add 2 bytes / line for line header */
157 .colorspace = V4L2_COLORSPACE_SRGB,
158 .priv = 1},
159 {352, 288, V4L2_PIX_FMT_PAC207, V4L2_FIELD_NONE,
160 .bytesperline = 352,
80544d3c
HG
161 /* compressed, but only when needed (not compressed
162 when the framerate is low) */
163 .sizeimage = (352 + 2) * 288,
c2446b3e
JFM
164 .colorspace = V4L2_COLORSPACE_SRGB,
165 .priv = 0},
e2997a72
HG
166};
167
168static const __u8 pac207_sensor_init[][8] = {
169 {0x10, 0x12, 0x0d, 0x12, 0x0c, 0x01, 0x29, 0xf0},
170 {0x00, 0x64, 0x64, 0x64, 0x04, 0x10, 0xf0, 0x30},
171 {0x00, 0x00, 0x00, 0x70, 0xa0, 0xf8, 0x00, 0x00},
e2997a72
HG
172 {0x32, 0x00, 0x96, 0x00, 0xA2, 0x02, 0xaf, 0x00},
173};
174
739570bb 175static int pac207_write_regs(struct gspca_dev *gspca_dev, u16 index,
e2997a72
HG
176 const u8 *buffer, u16 length)
177{
178 struct usb_device *udev = gspca_dev->dev;
179 int err;
e2997a72 180
739570bb 181 memcpy(gspca_dev->usb_buf, buffer, length);
e2997a72
HG
182
183 err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x01,
184 USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
739570bb
JFM
185 0x00, index,
186 gspca_dev->usb_buf, length, PAC207_CTRL_TIMEOUT);
e2997a72
HG
187 if (err < 0)
188 PDEBUG(D_ERR,
189 "Failed to write registers to index 0x%04X, error %d)",
190 index, err);
191
192 return err;
193}
194
195
903e10aa 196static int pac207_write_reg(struct gspca_dev *gspca_dev, u16 index, u16 value)
e2997a72
HG
197{
198 struct usb_device *udev = gspca_dev->dev;
199 int err;
200
201 err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x00,
202 USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
203 value, index, NULL, 0, PAC207_CTRL_TIMEOUT);
204 if (err)
205 PDEBUG(D_ERR, "Failed to write a register (index 0x%04X,"
206 " value 0x%02X, error %d)", index, value, err);
207
208 return err;
209}
210
903e10aa 211static int pac207_read_reg(struct gspca_dev *gspca_dev, u16 index)
e2997a72
HG
212{
213 struct usb_device *udev = gspca_dev->dev;
e2997a72
HG
214 int res;
215
216 res = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 0x00,
217 USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
739570bb
JFM
218 0x00, index,
219 gspca_dev->usb_buf, 1, PAC207_CTRL_TIMEOUT);
e2997a72
HG
220 if (res < 0) {
221 PDEBUG(D_ERR,
222 "Failed to read a register (index 0x%04X, error %d)",
223 index, res);
224 return res;
225 }
226
739570bb 227 return gspca_dev->usb_buf[0];
e2997a72
HG
228}
229
e2997a72
HG
230/* this function is called at probe time */
231static int sd_config(struct gspca_dev *gspca_dev,
232 const struct usb_device_id *id)
233{
4aa0d037 234 struct sd *sd = (struct sd *) gspca_dev;
e2997a72
HG
235 struct cam *cam;
236 u8 idreg[2];
237
238 idreg[0] = pac207_read_reg(gspca_dev, 0x0000);
239 idreg[1] = pac207_read_reg(gspca_dev, 0x0001);
240 idreg[0] = ((idreg[0] & 0x0F) << 4) | ((idreg[1] & 0xf0) >> 4);
241 idreg[1] = idreg[1] & 0x0f;
242 PDEBUG(D_PROBE, "Pixart Sensor ID 0x%02X Chips ID 0x%02X",
243 idreg[0], idreg[1]);
244
245 if (idreg[0] != 0x27) {
246 PDEBUG(D_PROBE, "Error invalid sensor ID!");
247 return -ENODEV;
248 }
249
e2997a72
HG
250 PDEBUG(D_PROBE,
251 "Pixart PAC207BCA Image Processor and Control Chip detected"
252 " (vid/pid 0x%04X:0x%04X)", id->idVendor, id->idProduct);
253
254 cam = &gspca_dev->cam;
e2997a72
HG
255 cam->cam_mode = sif_mode;
256 cam->nmodes = ARRAY_SIZE(sif_mode);
4aa0d037
JFM
257 sd->brightness = PAC207_BRIGHTNESS_DEFAULT;
258 sd->exposure = PAC207_EXPOSURE_DEFAULT;
259 sd->gain = PAC207_GAIN_DEFAULT;
66e4124f 260 sd->autogain = AUTOGAIN_DEF;
e2997a72
HG
261
262 return 0;
263}
264
012d6b02
JFM
265/* this function is called at probe and resume time */
266static int sd_init(struct gspca_dev *gspca_dev)
e2997a72 267{
66e4124f
JFM
268 pac207_write_reg(gspca_dev, 0x41, 0x00);
269 /* Bit_0=Image Format,
270 * Bit_1=LED,
271 * Bit_2=Compression test mode enable */
272 pac207_write_reg(gspca_dev, 0x0f, 0x00); /* Power Control */
e2997a72 273
e2997a72
HG
274 return 0;
275}
276
277/* -- start the camera -- */
72ab97ce 278static int sd_start(struct gspca_dev *gspca_dev)
e2997a72
HG
279{
280 struct sd *sd = (struct sd *) gspca_dev;
281 __u8 mode;
282
283 pac207_write_reg(gspca_dev, 0x0f, 0x10); /* Power control (Bit 6-0) */
284 pac207_write_regs(gspca_dev, 0x0002, pac207_sensor_init[0], 8);
285 pac207_write_regs(gspca_dev, 0x000a, pac207_sensor_init[1], 8);
286 pac207_write_regs(gspca_dev, 0x0012, pac207_sensor_init[2], 8);
c529e556 287 pac207_write_regs(gspca_dev, 0x0042, pac207_sensor_init[3], 8);
e2997a72
HG
288
289 /* Compression Balance */
290 if (gspca_dev->width == 176)
291 pac207_write_reg(gspca_dev, 0x4a, 0xff);
292 else
293 pac207_write_reg(gspca_dev, 0x4a, 0x88);
294 pac207_write_reg(gspca_dev, 0x4b, 0x00); /* Sram test value */
295 pac207_write_reg(gspca_dev, 0x08, sd->brightness);
296
297 /* PGA global gain (Bit 4-0) */
298 pac207_write_reg(gspca_dev, 0x0e, sd->gain);
299 pac207_write_reg(gspca_dev, 0x02, sd->exposure); /* PXCK = 12MHz /n */
300
301 mode = 0x02; /* Image Format (Bit 0), LED (1), Compr. test mode (2) */
d43fa32f 302 if (gspca_dev->width == 176) { /* 176x144 */
e2997a72
HG
303 mode |= 0x01;
304 PDEBUG(D_STREAM, "pac207_start mode 176x144");
d43fa32f 305 } else { /* 352x288 */
e2997a72 306 PDEBUG(D_STREAM, "pac207_start mode 352x288");
d43fa32f 307 }
e2997a72
HG
308 pac207_write_reg(gspca_dev, 0x41, mode);
309
310 pac207_write_reg(gspca_dev, 0x13, 0x01); /* Bit 0, auto clear */
311 pac207_write_reg(gspca_dev, 0x1c, 0x01); /* not documented */
6a7eba24 312 msleep(10);
e2997a72
HG
313 pac207_write_reg(gspca_dev, 0x40, 0x01); /* Start ISO pipe */
314
315 sd->sof_read = 0;
316 sd->autogain_ignore_frames = 0;
317 atomic_set(&sd->avg_lum, -1);
72ab97ce 318 return 0;
e2997a72
HG
319}
320
321static void sd_stopN(struct gspca_dev *gspca_dev)
322{
323 pac207_write_reg(gspca_dev, 0x40, 0x00); /* Stop ISO pipe */
324 pac207_write_reg(gspca_dev, 0x41, 0x00); /* Turn of LED */
325 pac207_write_reg(gspca_dev, 0x0f, 0x00); /* Power Control */
326}
327
8a5b2e90
HG
328/* Include pac common sof detection functions */
329#include "pac_common.h"
330
e2997a72
HG
331static void pac207_do_auto_gain(struct gspca_dev *gspca_dev)
332{
333 struct sd *sd = (struct sd *) gspca_dev;
e2997a72
HG
334 int avg_lum = atomic_read(&sd->avg_lum);
335
dcef3237 336 if (avg_lum == -1)
e2997a72
HG
337 return;
338
dcef3237 339 if (sd->autogain_ignore_frames > 0)
e2997a72 340 sd->autogain_ignore_frames--;
dcef3237
HG
341 else if (gspca_auto_gain_n_exposure(gspca_dev, avg_lum,
342 100 + sd->brightness / 2, PAC207_AUTOGAIN_DEADZONE,
343 PAC207_GAIN_KNEE, PAC207_EXPOSURE_KNEE))
8a5b2e90 344 sd->autogain_ignore_frames = PAC_AUTOGAIN_IGNORE_FRAMES;
e2997a72
HG
345}
346
e2997a72
HG
347static void sd_pkt_scan(struct gspca_dev *gspca_dev,
348 struct gspca_frame *frame,
a5ae2062 349 __u8 *data,
e2997a72
HG
350 int len)
351{
ab8f12cf 352 struct sd *sd = (struct sd *) gspca_dev;
e2997a72 353 unsigned char *sof;
e2997a72 354
327c4abf 355 sof = pac_find_sof(gspca_dev, data, len);
e2997a72 356 if (sof) {
ab8f12cf
HG
357 int n;
358
e2997a72 359 /* finish decoding current frame */
ab8f12cf 360 n = sof - data;
327c4abf
HG
361 if (n > sizeof pac_sof_marker)
362 n -= sizeof pac_sof_marker;
ab8f12cf
HG
363 else
364 n = 0;
365 frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame,
366 data, n);
367 sd->header_read = 0;
6a7eba24 368 gspca_frame_add(gspca_dev, FIRST_PACKET, frame, NULL, 0);
e2997a72
HG
369 len -= sof - data;
370 data = sof;
371 }
ab8f12cf
HG
372 if (sd->header_read < 11) {
373 int needed;
e2997a72 374
ab8f12cf
HG
375 /* get average lumination from frame header (byte 5) */
376 if (sd->header_read < 5) {
377 needed = 5 - sd->header_read;
378 if (len >= needed)
379 atomic_set(&sd->avg_lum, data[needed - 1]);
380 }
381 /* skip the rest of the header */
382 needed = 11 - sd->header_read;
383 if (len <= needed) {
384 sd->header_read += len;
385 return;
386 }
387 data += needed;
388 len -= needed;
389 sd->header_read = 11;
390 }
e2997a72 391
ab8f12cf 392 gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len);
e2997a72
HG
393}
394
395static void setbrightness(struct gspca_dev *gspca_dev)
396{
397 struct sd *sd = (struct sd *) gspca_dev;
398
399 pac207_write_reg(gspca_dev, 0x08, sd->brightness);
400 pac207_write_reg(gspca_dev, 0x13, 0x01); /* Bit 0, auto clear */
401 pac207_write_reg(gspca_dev, 0x1c, 0x01); /* not documented */
402}
403
404static void setexposure(struct gspca_dev *gspca_dev)
405{
406 struct sd *sd = (struct sd *) gspca_dev;
407
408 pac207_write_reg(gspca_dev, 0x02, sd->exposure);
409 pac207_write_reg(gspca_dev, 0x13, 0x01); /* Bit 0, auto clear */
410 pac207_write_reg(gspca_dev, 0x1c, 0x01); /* not documented */
411}
412
413static void setgain(struct gspca_dev *gspca_dev)
414{
415 struct sd *sd = (struct sd *) gspca_dev;
416
417 pac207_write_reg(gspca_dev, 0x0e, sd->gain);
418 pac207_write_reg(gspca_dev, 0x13, 0x01); /* Bit 0, auto clear */
419 pac207_write_reg(gspca_dev, 0x1c, 0x01); /* not documented */
420}
421
422static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
423{
424 struct sd *sd = (struct sd *) gspca_dev;
425
426 sd->brightness = val;
427 if (gspca_dev->streaming)
428 setbrightness(gspca_dev);
429 return 0;
430}
431
432static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val)
433{
434 struct sd *sd = (struct sd *) gspca_dev;
435
436 *val = sd->brightness;
437 return 0;
438}
439
440static int sd_setexposure(struct gspca_dev *gspca_dev, __s32 val)
441{
442 struct sd *sd = (struct sd *) gspca_dev;
443
e2997a72
HG
444 sd->exposure = val;
445 if (gspca_dev->streaming)
446 setexposure(gspca_dev);
447 return 0;
448}
449
450static int sd_getexposure(struct gspca_dev *gspca_dev, __s32 *val)
451{
452 struct sd *sd = (struct sd *) gspca_dev;
453
454 *val = sd->exposure;
455 return 0;
456}
457
458static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val)
459{
460 struct sd *sd = (struct sd *) gspca_dev;
461
e2997a72
HG
462 sd->gain = val;
463 if (gspca_dev->streaming)
464 setgain(gspca_dev);
465 return 0;
466}
467
468static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val)
469{
470 struct sd *sd = (struct sd *) gspca_dev;
471
472 *val = sd->gain;
473 return 0;
474}
475
476static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val)
477{
478 struct sd *sd = (struct sd *) gspca_dev;
479
480 sd->autogain = val;
481 /* when switching to autogain set defaults to make sure
482 we are on a valid point of the autogain gain /
483 exposure knee graph, and give this change time to
484 take effect before doing autogain. */
485 if (sd->autogain) {
486 sd->exposure = PAC207_EXPOSURE_DEFAULT;
487 sd->gain = PAC207_GAIN_DEFAULT;
488 if (gspca_dev->streaming) {
489 sd->autogain_ignore_frames =
8a5b2e90 490 PAC_AUTOGAIN_IGNORE_FRAMES;
e2997a72
HG
491 setexposure(gspca_dev);
492 setgain(gspca_dev);
493 }
494 }
495
496 return 0;
497}
498
499static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val)
500{
501 struct sd *sd = (struct sd *) gspca_dev;
502
503 *val = sd->autogain;
504 return 0;
505}
506
507/* sub-driver description */
a5ae2062 508static const struct sd_desc sd_desc = {
e2997a72
HG
509 .name = MODULE_NAME,
510 .ctrls = sd_ctrls,
511 .nctrls = ARRAY_SIZE(sd_ctrls),
512 .config = sd_config,
012d6b02 513 .init = sd_init,
e2997a72
HG
514 .start = sd_start,
515 .stopN = sd_stopN,
e2997a72
HG
516 .dq_callback = pac207_do_auto_gain,
517 .pkt_scan = sd_pkt_scan,
518};
519
520/* -- module initialisation -- */
a5ae2062 521static const __devinitdata struct usb_device_id device_table[] = {
9d64fdb1
JFM
522 {USB_DEVICE(0x041e, 0x4028)},
523 {USB_DEVICE(0x093a, 0x2460)},
87945895 524 {USB_DEVICE(0x093a, 0x2461)},
9d64fdb1
JFM
525 {USB_DEVICE(0x093a, 0x2463)},
526 {USB_DEVICE(0x093a, 0x2464)},
527 {USB_DEVICE(0x093a, 0x2468)},
528 {USB_DEVICE(0x093a, 0x2470)},
529 {USB_DEVICE(0x093a, 0x2471)},
530 {USB_DEVICE(0x093a, 0x2472)},
db91235e 531 {USB_DEVICE(0x093a, 0x2474)},
d654dca7 532 {USB_DEVICE(0x093a, 0x2476)},
91711874 533 {USB_DEVICE(0x145f, 0x013a)},
9d64fdb1 534 {USB_DEVICE(0x2001, 0xf115)},
e2997a72
HG
535 {}
536};
537MODULE_DEVICE_TABLE(usb, device_table);
538
539/* -- device connect -- */
540static int sd_probe(struct usb_interface *intf,
541 const struct usb_device_id *id)
542{
d43fa32f
JFM
543 return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd),
544 THIS_MODULE);
e2997a72
HG
545}
546
547static struct usb_driver sd_driver = {
548 .name = MODULE_NAME,
549 .id_table = device_table,
550 .probe = sd_probe,
551 .disconnect = gspca_disconnect,
6a709749
JFM
552#ifdef CONFIG_PM
553 .suspend = gspca_suspend,
554 .resume = gspca_resume,
555#endif
e2997a72
HG
556};
557
558/* -- module insert / remove -- */
559static int __init sd_mod_init(void)
560{
f69e9529
AK
561 int ret;
562 ret = usb_register(&sd_driver);
563 if (ret < 0)
e6b14849 564 return ret;
903e10aa 565 PDEBUG(D_PROBE, "registered");
e2997a72
HG
566 return 0;
567}
568static void __exit sd_mod_exit(void)
569{
570 usb_deregister(&sd_driver);
571 PDEBUG(D_PROBE, "deregistered");
572}
573
574module_init(sd_mod_init);
575module_exit(sd_mod_exit);