/* * USB Biopod driver - 0.1 * Author: Mike Smith, Micah Villmow * Copyright 2005 - Mike Smith and Micah Villmow - All Rights Reserved * Purpose: To provide a driver interface for the Authentec 3400/3500 * chip inside the APC biopod biometric thumb scanner. */ #include "biopod.h" /* Define these values to match your devices */ #define USB_BIOPOD_VENDOR_ID 0x08ff #define USB_BIOPOD_PRODUCT_ID 0x5731 /* Get a minor range for your devices from the usb maintainer */ #define USB_BIOPOD_MINOR_BASE 192 #define TO_WAIT_ON_BULK_IN 10 #define BULK_PACKET_SIZE 64 #define able_to_lock(dev) (down_interruptible(&((dev)->biopod_mutex))) #define unlock(dev) (up(&((dev)->biopod_mutex))) #define initialization_has_been_fully_done_on_biopod(dev) \ (!(dev)->four_to_eight || !(dev->pixel_table)) #define num_seconds(x) ((x) * HZ) #define already_exist(x) (!(x)) /*--------------------------------------------------------------------------*/ #define free_matrix_data(m, rows) \ { int r;\ if (m) for (r = 0; r < rows; (r)++) if ((m)[r]) {\ PDEBUG("Trying to free matrix row %d\n",r);kfree((m)[r]);PDEBUG("Success!\n");\ }\ } #define free_matrix(m) \ free_matrix_data((m)->data, (m)->num_rows) \ #define free_biopod_histograms(h) \ { \ int i; \ if ((h)->data) { \ for (i = 0; i < BIOPOD_NUM_HISTOGRAMS; i++) \ if (((h)->data[i]).bins) { \ PDEBUG("Trying to free histogram data[%d]->bins\n",i); \ kfree(((h)->data[i]).bins); \ PDEBUG("Success!\n"); \ } \ PDEBUG("Trying to free histograms!\n"); \ kfree((h)->data); \ PDEBUG("Success!\n"); \ } \ } #define free_biopod_register_vals_interface(i) \ if (((i)->in).vals) {\ PDEBUG("Trying to free in interface\n");\ kfree(((i)->in).vals); \ PDEBUG("Success!\n");\ }\ if (((i)->out).vals){\ PDEBUG("Trying to free out interface\n");\ kfree(((i)->out).vals); \ PDEBUG("Success!\n");\ } #define free_biopod_sensor_calibrator_resistors(r) \ if (((r)->res)) {\ PDEBUG("Trying to free resistor -> res\n");\ kfree((r)->res); \ PDEBUG("Success!\n");\ } #define free_biopod_sensor_calibrator(c) \ free_biopod_sensor_calibrator_resistors(&((c)->resistors)) \ #define free_biopod_sensor_fields(s) \ if ((s)->data){ \ PDEBUG("Trying to free sensor field data!\n"); \ kfree((s)->data); \ PDEBUG("Success!\n");\ } #define free_biopod_image(i) \ {\ if((*i).data) \ PDEBUG("Trying to free image data!\n");\ kfree((*i).data);\ PDEBUG("Success!\n"); \ } #define free_all_biopod_image_data_in(i, some_free_var) \ free_image(i, some_free_var) #define free_all_biopod_frame_data_in(f) \ free_biopod_histograms(&((f)->histos)) \ free_biopod_image(&((f)->image)) \ free_biopod_register_vals_interface(&((f)->regs)) #define free_all_biopod_bulk_in_data_in(bi) \ if(bi.buffer){\ PDEBUG("Trying to free bulk in buffer....\n"); \ kfree(bi.buffer); \ PDEBUG("Success!\n");\ }\ if(bi.data){\ PDEBUG("Trying to free bulk in data...\n"); \ kfree(bi.data); \ PDEBUG("Success!\n");\ } #define free_all_biopod_sensor_data_in(s) \ free_biopod_sensor_fields(&((s)->fields)) \ free_biopod_sensor_calibrator(&((s)->calibrator)) #define free_all_biopod_member_data_in(dev) \ free_all_biopod_bulk_in_data_in(((*dev).bulk_in)) \ free_all_biopod_sensor_data_in(&((dev)->sensor)) \ free_all_biopod_frame_data_in(&(dev->frame)) #define create_matrix_data(m, rows, cols, retval, error) \ { \ if(m) { \ PDEBUG("Passing in non-null pointer to create_matrix_data is BAD!!!%s:%d\n",__FILE__,__LINE__);\ retval = -1;\ } else { \ int r = 0; \ m = kmalloc(sizeof(*m) * rows, GFP_KERNEL); \ if(!m) { \ retval = -1; \ PDEBUG("%s:%d - Allocation of rows for matrix failed!\n",__FILE__,__LINE__);\ } else { \ for(r = 0; r < rows; r++) { \ m[r] = kmalloc(sizeof(**m) * cols, GFP_KERNEL); \ if(!m[r]) { \ PDEBUG("Allocation of row in matrix failed! rows = %d, row = %d, retval = %d, error = %d\n",rows,r,retval,error);\ rows = r; retval = error = -1; \ break; \ } \ } \ if(!error) { \ for(r = 0; r < rows; r++) { \ for(error = 0; error < cols; error++) {\ m[r][error] = 0; \ } \ } \ } } \ } \ } #define create_matrix(m, retval, error) \ if (!(m)){ retval = -1; \ PDEBUG("Passing in null pointer to a function is BAD!!!%s:%d\n",__FILE__,__LINE__);\ }\ else { \ create_matrix_data((m)->data, (m)->num_rows, \ (m)->num_cols,retval, error) \ } #define zero_matrix(m, r, c) \ for(r=0;r<(m)->num_rows;r++)for(c=0;c<(m)->num_cols;c++)(m)->data[r][c]=0; /* table of devices that work with this driver */ static struct usb_device_id biopod_table [] = { { USB_DEVICE(USB_BIOPOD_VENDOR_ID, USB_BIOPOD_PRODUCT_ID) }, { } /* Terminating entry */ }; MODULE_DEVICE_TABLE (usb, biopod_table); /* Get a minor range for your devices from the usb maintainer */ #define USB_BIOPOD_MINOR_BASE 192 static struct usb_driver biopod_driver; /* Set default values for the sensor */ static int able_to_set_default_vals_for_sensor(biopod_sensor *s) { if (!s) return 1; s->input_length = 10240; s->output_length = 256; s->dpi = 500; /*s->packed_image_type = AT_PACKED_500DPI_IMAGE;*/ /*not used right now */ s->num_cols = 128; s->num_rows = 128; s->bits_per_pixel = 4; /*(s->fields).num = BIOPOD_NUM_SENSOR_FIELDS;*/ /*(s->regs).num = BIOPOD_NUM_SENSOR_REGS;*/ s->num_regs_to_send = 44; /*s->num_hist_bins = BIOPOD_NUM_SENSOR_HIST_BINS;*/ /*s->num_subarrays = BIOPOD_NUM_SENSOR_SUBARRAYS;;*/ s->num_channels = BIOPOD_NUM_SENSOR_ANALOG_CHANNELS; (s->chip_rev).begin = 0x42; (s->chip_rev).end = 0x4F; s->reg80_mask = 0x04; s->reg81_mask = 0x61; (s->calibrator).cap_red = 3; (s->calibrator).resistors.res[0] = 0; (s->calibrator).resistors.res[1] = 7; set_status(s, HAS_LTNG_RODS); clear_status(s, IS_CARRIER_NULL_INVERTED); set_status(s, IS_PHASE_SHIFTED); set_status(s, IS_COL_SCAN_RATE_FIXED); (s->lr).row.begin = 8; (s->lr).row.begin = 8; (s->lr).row.begin = 16; (s->lr).row.begin = 16; (s->lr).copy_row_offset = +1; return 0; } /* Set default values for the sensor fields */ static int able_to_set_default_vals_for_fields(biopod_sensor_field *f) { if (!f) return 1; /* 0, Unused. */ f[0].reg_num = 0xFF; f[0].start_bit = 0; f[0].end_bit = 0; /* 1, BIOPOD_SENSOR_GAIN_1,Gain rate multiple in 1st stage. */ /* .. 0 = 2x, 1=4x, 2=8x, 3=16x */ f[1].reg_num = 0x8E; f[1].start_bit = 0; f[1].end_bit = 1; /* 2, BIOPOD_SENSOR_GAIN_2, Gain rate multiple in 2nd stage. */ /* .. 0 = 2x, 1=4x, 2=8x, 3=16x */ f[2].reg_num = 0x8E; f[2].start_bit = 4; f[2].end_bit = 5; /* 3, BIOPOD_SENSOR_GAIN, Sensor gain (Combination of */ /* .. sensor gains 1 & 2. No longer used.) */ f[3].reg_num = 0xFF; f[3].start_bit = 0; f[3].end_bit = 0; /* 4, BIOPOD_CARRIER_NULL, Carrier Null (15..0) 0...-3.8V */ f[4].reg_num = 0x90; f[4].start_bit = 0; f[4].end_bit = 3; /* 5, BIOPOD_CARRIER_NULL_E, Carrier Null Enable */ f[5].reg_num = 0x90; f[5].start_bit = 4; f[5].end_bit = 4; /* 6, BIOPOD_AD_REF_HI, A/D Reference Hi (0-31).15625-5V */ f[6].reg_num = 0x91; f[6].start_bit = 0; f[6].end_bit = 6; /* 7, BIOPOD_AD_REF_LO, A/D Reference Lo (0-31).15625-5V */ f[7].reg_num = 0x92; f[7].start_bit = 0; f[7].end_bit = 6; /* 8, BIOPOD_FINGER_DETECT, Finger Detect Status bit, 1=ON */ f[8].reg_num = 0x9B; f[8].start_bit = 0; f[8].end_bit = 0; /* 9, BIOPOD_MEASURE_FREQ, Measure Frequency(0-7)125KHz-2MHz*/ f[9].reg_num = 0x8A; f[9].start_bit = 0; f[9].end_bit = 2; /* 10, BIOPOD_MEASURE_DRIVE, Measure Drive (0-3) 0.3/1/2/4V */ f[10].reg_num = 0x89; f[10].start_bit = 0; f[10].end_bit = 1; /* 11, BIOPOD_DETECT_FREQ, Detect Frequency(0-7) 125KHz-2Mhz*/ f[11].reg_num = 0x87; f[11].start_bit = 0; f[11].end_bit = 2; /* 12, BIOPOD_DETECT_DRIVE, Detect drive (0-3) 0.3/1/2/4V */ f[12].reg_num = 0x86; f[12].start_bit = 0; f[12].end_bit = 1; /* 13, BIOPOD_MAX_RES, Use max phase resolution */ f[13].reg_num = 0x00; f[13].start_bit = 0; f[13].end_bit = 0; /* 14, BIOPOD_COLUMN_SCAN_PER, Column Scan period (0-7) 64 uS */ /* .. to 2,048 uS. */ f[14].reg_num = 0x88; f[14].start_bit = 0; f[14].end_bit = 2; /* 15, BIOPOD_FING_DETECT_CAP, Finger detect capacitor (0-127) */ /* .. 93.75ff to 11.8 pf */ f[15].reg_num = 0x85; f[15].start_bit = 0; f[15].end_bit = 6; /* 16, BIOPOD_FING_DETECT_RES, Finger detect resistor (0-7) */ /* .. 500 Ohms to 4K Ohms */ f[16].reg_num = 0x84; f[16].start_bit = 0; f[16].end_bit = 2; /* 17, BIOPOD_Z_MATRIX_ENABLE, Z-matrix enable. */ f[17].reg_num = 0x8B; f[17].start_bit = 4; f[17].end_bit = 4; /* 18, BIOPOD_DEMOD_PHASE, Demodulation Phase. */ f[18].reg_num = 0x8C; f[18].start_bit = 0; f[18].end_bit = 6; /* 19, BIOPOD_REVISION_NUMBER, Chip revision number (chip type)*/ f[19].reg_num = 0x9D; f[19].start_bit = 0; f[19].end_bit = 6; /* 20, BIOPOD_START_SCAN_ROW, Starting Image Scan Row. */ f[20].reg_num = 0x93; f[20].start_bit = 0; f[20].end_bit = 3; /* 21, BIOPOD_END_SCAN_ROW, Ending Image Scan Row. */ f[21].reg_num = 0x94; f[21].start_bit = 0; f[21].end_bit = 3; /* 22, BIOPOD_DRIVE_BOOST, Drive Boost Bit. */ f[22].reg_num = 0x82; f[22].start_bit = 4; f[22].end_bit = 4; /* 23, BIOPOD_NORM_PHASE, "Normalized" Phase (no longer used.) */ f[23].reg_num = 0xFF; f[23].start_bit = 0; f[23].end_bit = 0; /* 24, BIOPOD_IMAGE_DATA_DISABLE, Image Data Disable. */ f[24].reg_num = 0x98; f[24].start_bit = 0; f[24].end_bit = 0; /* 25, BIOPOD_HISTOGRAM_EACH_ROW, Histogram each row (subarray)*/ f[25].reg_num = 0x98; f[25].start_bit = 2; f[25].end_bit = 2; /* 26, BIOPOD_MASTER_RESET, Master reset bit. */ f[26].reg_num = 0x80; f[26].start_bit = 0; f[26].end_bit = 0; /* 27, BIOPOD_SET_ONE_SHOT, One shot bit for a single scan. */ f[27].reg_num = 0x81; f[27].start_bit = 2; f[27].end_bit = 2; /* 28, BIOPOD_READ_REGISTERS, Read registers bit. */ f[28].reg_num = 0x81; f[28].start_bit = 1; f[28].end_bit = 1; /* 29, BIOPOD_CONTINUOUS_SCAN, Continuous scan bit. */ f[29].reg_num = 0x81; f[29].start_bit = 0; f[29].end_bit = 0; /* 30, BIOPOD_ASYNC_REGISTER_UPD, Update registers continuously*/ f[30].reg_num = 0x80; f[30].start_bit = 2; f[30].end_bit = 2; /* 31, BIOPOD_EXCITATION_BIAS, Excitation bias current. */ f[31].reg_num = 0x00; f[31].start_bit = 0; f[31].end_bit = 0; /* 32, BIOPOD_DEBOUNCE_DELAY, Finger detect debounce delay. */ f[32].reg_num = 0x83; f[32].start_bit = 4; f[32].end_bit = 5; /* 33, BIOPOD_INTEGRATION_TIME, Scan integration time. */ f[33].reg_num = 0x00; f[33].start_bit = 0; f[33].end_bit = 0; /* 34, BIOPOD_SENSE_AMP_BIAS, Bias current for sense amps.*/ f[34].reg_num = 0x00; f[34].start_bit = 0; f[34].end_bit = 0; /* 35, BIOPOD_Z_MATRIX_VPATT, Z-Matrix vertical pattern. */ f[35].reg_num = 0x8B; f[35].start_bit = 2; f[35].end_bit = 3; /* 36, BIOPOD_Z_MATRIX_HPATT, Z-Matrix horizontal pattern.*/ f[36].reg_num = 0x8B; f[36].start_bit = 0; f[36].end_bit = 1; /* 37, BIOPOD_DEMOD_PHASE_1, Demodulation phase 1. */ f[37].reg_num = 0x8D; f[37].start_bit = 0; f[37].end_bit = 6; /* 38, BIOPOD_DEMOD_PHASE_2, Demodulation phase 2. */ f[38].reg_num = 0x8C; f[38].start_bit = 0; f[38].end_bit = 6; /* 39, BIOPOD_Z_MATRIX_POWER, Z-Matrix power mode, 1=all times */ f[39].reg_num = 0x00; f[39].start_bit = 0; f[39].end_bit = 0; /* 40, BIOPOD_SAMPLE_HOLD_BIAS, Sample/hold bias current. */ f[40].reg_num = 0x00; f[40].start_bit = 0; f[40].end_bit = 0; /* 41, BIOPOD_ANALOG_CHAN_BIAS, Analog channel bias current. */ f[41].reg_num = 0x00; f[41].start_bit = 0; f[41].end_bit = 0; /* 42, BIOPOD_START_COL, Starting column number. */ f[42].reg_num = 0x95; f[42].start_bit = 0; f[42].end_bit = 6; /* 43, BIOPOD_END_COL, Ending column number. */ f[43].reg_num = 0x96; f[43].start_bit = 0; f[43].end_bit = 6; /* 44, BIOPOD_DATA_FORMAT, Data format, 0=normal */ f[44].reg_num = 0x00; f[44].start_bit = 0; f[44].end_bit = 0; /* 45, BIOPOD_THRESHOLD, Threshold value for binary mode.*/ f[45].reg_num = 0x97; f[45].start_bit = 0; f[45].end_bit = 3; /* 46, BIOPOD_TEST_REG_ENABLE, Enable sending of test registers*/ f[46].reg_num = 0x98; f[46].start_bit = 5; f[46].end_bit = 5; /* 47, BIOPOD_HISTOGRAM_FULL_ARY, Histogram over full array. */ f[47].reg_num = 0x98; f[47].start_bit = 3; f[47].end_bit = 3; /* 48, BIOPOD_HISTOGRAM_ENABLE, Hardware histogram enable. */ f[48].reg_num = 0x98; f[48].start_bit = 1; f[48].end_bit = 1; /* 49, BIOPOD_GPO_0, General-purpose output (LED) 0.*/ f[49].reg_num = 0x99; f[49].start_bit = 0; f[49].end_bit = 0; /* 50, BIOPOD_GPO_1, General-purpose output (LED) 1.*/ f[50].reg_num = 0x99; f[50].start_bit = 1; f[50].end_bit = 1; /* 51, BIOPOD_GPO_2, General-purpose output (LED) 2.*/ f[51].reg_num = 0x99; f[51].start_bit = 2; f[51].end_bit = 2; /* 52, BIOPOD_GPO_3, General-purpose output (LED) 3.*/ f[52].reg_num = 0x99; f[52].start_bit = 3; f[52].end_bit = 3; /* 53, BIOPOD_Z_MATRIX_DOUBLER, Z-Matrix doubler enable. */ f[53].reg_num = 0x8B; f[53].start_bit = 5; f[53].end_bit = 5; /* 54, BIOPOD_CHALLENGE_1, Challenge word -- 1 of 5. */ f[54].reg_num = 0x9B; f[54].start_bit = 0; f[54].end_bit = 6; /* 55, BIOPOD_CHALLENGE_2, Challenge word -- 2 of 5. */ f[55].reg_num = 0x9C; f[55].start_bit = 0; f[55].end_bit = 6; /* 56, BIOPOD_CHALLENGE_3, Challenge word -- 3 of 5. */ f[56].reg_num = 0x9D; f[56].start_bit = 0; f[56].end_bit = 6; /* 57, BIOPOD_CHALLENGE_4, Challenge word -- 4 of 5. */ f[57].reg_num = 0x9E; f[57].start_bit = 0, f[57].end_bit = 6; /* 58, BIOPOD_CHALLENGE_5, Challenge word -- 5 of 5. */ f[58].reg_num = 0x9F; f[58].start_bit = 0; f[58].end_bit = 6; /* 59, BIOPOD_EXCITATION_SQUARE, Use square wave. */ f[59].reg_num = 0x82; f[59].start_bit = 5; f[59].end_bit = 5; /* 60, BIOPOD_FORCE_FINGER_ON, Force finger detect. */ f[60].reg_num = 0xA2; f[60].start_bit = 1; f[60].end_bit = 1; /* 61, BIOPOD_SSI_INT_ENABLE, Enable Sync. Serial Int.*/ f[61].reg_num = 0x81; f[61].start_bit = 6; f[61].end_bit = 6; return 0; } /* Set default values for the sensor registers */ static int able_to_set_default_vals_for_regs(biopod_sensor_registers *r) { int i; r->runtime = kmalloc(60 * sizeof(*r->runtime),GFP_KERNEL); if(!r->runtime) return 1; for(i = 0; i < 60; i++) r->runtime[i] = 0; r->defaults = &default_registers_3500[0]; /* 0x80 Control Register 1 */ /* 0x81 Control Register 2 */ /* 0x82 Excitation Common Controls */ /* 0x83 Detect Control */ /* 0x84 Reference Resistor */ /* 0x85 Reference Capacitor */ /* 0x86 Detect Drive */ /* 0x87 Detect Frequency */ /* 0x88 Column Scan Rate */ /* 0x89 Measure Drive */ /* 0x8A Measure Frequency */ /* 0x8B Z-Matrix Kernel Size */ /* 0x8C Demod Phase 2 */ /* was 04 */ /* 0x8D Demod Phase 1 */ /* 0x8E Channel Gain */ /* 0x8F Channel Bias */ /* 0x90 Carrier Null */ /* 0x91 A/D Reference High */ /* 0x92 A/D Reference Low */ /* 0x93 Start Row */ /* 0x94 End Row*/ /* 0x95 Start Column */ /* 0x96 End Column * */ /* 0x97 Data Format */ /* 0x98 ** Was 20 ** Image Data Control * */ /* 0x99 LED Control */ /* 0x9A Status */ /* 0x9B Challenge Word 1 */ /* 0x9C Challenge Word 2 */ /* 0x9D Challenge Word 3 */ /* 0x9E Challenge Word 4 */ /* 0x9F Challenge Word 5 */ /* 0xA0 Bias */ /* 0xA1 */ /* 0xA2 */ /* 0xA3 */ /* 0xA4 */ /* 0xA5 */ /* 0xA6 */ /* 0xA7 (Array Float bit should be OFF for 3000 series.) */ /* 0xA8 */ /* 0xA9 */ /* 0xAA */ /* 0xAB */ /* 0xAC */ /* 0xAD */ /* 0xAE */ /* 0xAF */ /* 0xB0 */ /* 0xB1 */ /* 0xB2 */ /* 0xB3 */ /* 0xB4 */ /* 0xB5 */ /* 0xB6 */ /* 0xB7 */ return 0; } /* Create an image */ static int able_to_create_biopod_image(biopod_image *m) { int retval = 0; if(!m) { PDEBUG("Passing in null pointer to a function is BAD!!!%s:%d\n",__FILE__,__LINE__); return 1; } init_MUTEX(&(m->image_mutex)); m->num_rows = BIOPOD_IMAGE_NUM_ROWS; m->num_cols = BIOPOD_IMAGE_NUM_COLS; m->data = kmalloc(sizeof(*(m->data)) * m->num_cols * m->num_rows, GFP_KERNEL); if(!(m->data)) { PDEBUG("create matrix for biopod image failed!\n"); retval = 1; } return retval; } /* Create a histogram */ static int able_to_create_biopod_histogram(biopod_histogram *h) { int i; if (!h) { PDEBUG("Passing in null pointer to a function is BAD!!!%s:%d\n",__FILE__,__LINE__); return 1; } h->count = 0; h->num_bins = BIOPOD_HISTOGRAM_NUM_BINS; h->bytes_per_pixel = BIOPOD_HISTOGRAM_BYTES_PER_PIXEL; h->bins = kmalloc(sizeof(*(h->bins)) * BIOPOD_HISTOGRAM_NUM_BINS,GFP_KERNEL); if (!(h->bins)) { return 1; } for (i = 0; i < BIOPOD_HISTOGRAM_NUM_BINS; i++) (h->bins)[i] = 0; return 0; } /* Create histograms */ static int able_to_create_biopod_histograms(biopod_histograms *h) { int i; if (!h) { PDEBUG("Passing in null pointer to a function is BAD!!!%s:%d\n",__FILE__,__LINE__); return 1; } init_MUTEX(&(h->histogram_mutex)); h->count = 0; h->num = BIOPOD_NUM_HISTOGRAMS; if (!(h->data = kmalloc(sizeof(*(h->data)) * BIOPOD_NUM_HISTOGRAMS, GFP_KERNEL))) { return 1; } for (i = 0; i < BIOPOD_NUM_HISTOGRAMS; i++) if (able_to_create_biopod_histogram(&((h->data)[i]))) { return 1; } return 0; } /* Create register_vals interface */ static int able_to_create_biopod_register_vals_interface (biopod_register_vals_interface *i) { int j; if (!i) { PDEBUG("Passing in null pointer to a function is BAD!!!%s:%d\n",__FILE__,__LINE__); return 1; } init_MUTEX(&(i->register_intf_mutex)); (i->in).num = (i->out).num = BIOPOD_NUM_SENSOR_REGS; i->in.vals = kmalloc(sizeof( *(i->in.vals)) * i->in.num, GFP_KERNEL); if(!i->in.vals) { PDEBUG("Input vals was not allocated correctly!\n"); goto error; } i->out.vals = kmalloc(sizeof( *(i->out.vals)) * i->out.num, GFP_KERNEL); if(!i->out.vals) { PDEBUG("Output vals was not allocated correctly!\n"); goto error; } for (j = 0; j < (i->in).num; j++) (i->in).vals[j] = 0; for (j = 0; j < (i->out).num; j++) (i->out).vals[j] = 0; return 0; error: return 1; } /* Create calibrator resistors */ static int able_to_create_biopod_sensor_calibrator_resistors (biopod_sensor_calibrator_resistors *r) { int i; if (!r) { PDEBUG("Passing in null pointer to a function is BAD!!!%s:%d\n",__FILE__,__LINE__); return 1; } r->num = BIOPOD_NUM_CAL_RESISTORS; r->res = kmalloc(sizeof(*(r->res)) * r->num, GFP_KERNEL); if(!r->res) { PDEBUG("Allocation of resistor array failed!\n"); goto error; } for(i = 0; i < r->num; i++) r->res[i] = 0; return 0; error: return 1; } /* Create calibrator*/ static int able_to_create_biopod_sensor_calibrator (biopod_sensor_calibrator *c) { if (!c) { PDEBUG("Passing in null pointer to a function is BAD!!!%s:%d\n",__FILE__,__LINE__); return 1; } c->cap_red = 0; if (able_to_create_biopod_sensor_calibrator_resistors(&c->resistors)) { PDEBUG("Creation of biopod_sensor_calibrator resistors failed!\n"); return 1; } return 0; } /* Create biopod sensor registers */ static int able_to_create_biopod_sensor_registers (biopod_sensor_registers *s) { if(!s) { PDEBUG("Passing in null pointer to a function is BAD!!!%s:%d\n",__FILE__,__LINE__); return 1; } s->num = BIOPOD_NUM_SENSOR_REGS; if(able_to_set_default_vals_for_regs(s)) { PDEBUG("Creation of default vals for regs failed!\n"); goto error; } return 0; error: return 1; } /* Create biopod sensor fields */ static int able_to_create_biopod_sensor_fields (biopod_sensor_fields *s) { if(!s) { PDEBUG("Passing in null pointer to a function is BAD!!!%s:%d\n",__FILE__,__LINE__); return 1; } s->num = BIOPOD_NUM_SENSOR_REGS; s->data = kmalloc(sizeof(*(s->data)) * s->num, GFP_KERNEL); if(!s->data) { PDEBUG("Allocation of sensor field data failed!\n"); goto error; } if(able_to_set_default_vals_for_fields(s->data)) { PDEBUG("Setting default values for sensor field data failed!\n"); goto error; } return 0; error: return 1; } /* Create all necessary things for the frame */ static int able_to_create_biopod_frame_for(usb_biopod *dev) { if (!dev) { /* this code should never happen because if dev is NULL, then it should have failed out alot earlier */ PDEBUG("Passing in null pointer to a function is BAD!!!%s:%d\n",__FILE__,__LINE__); return 1; } dev->frame.image.dev = dev; if(able_to_create_biopod_image(&(dev->frame.image))) { PDEBUG("creation of biopod image failed!\n"); goto error; } if(able_to_create_biopod_histograms(&(dev->frame.histos))) { PDEBUG("Creation of biopod histograms failed!\n"); goto error; } if(able_to_create_biopod_register_vals_interface(&(dev->frame.regs))) { PDEBUG("Creation of register value interface failed!\n"); goto error; } return 0; error: return 1; } /* Create all necessary things for the sensor */ static int able_to_create_biopod_sensor_for(usb_biopod *dev) { biopod_sensor *s; if (!dev){ PDEBUG("Passing in null pointer to a function is BAD!!!%s:%d\n",__FILE__,__LINE__); return 1; } s = &(dev->sensor); if (able_to_create_biopod_sensor_registers(&(s->regs))) { PDEBUG("Creation of biopod sensor registers failed!\n"); goto error; } if(able_to_create_biopod_sensor_fields(&(s->fields))) { PDEBUG("Creation of biopod sensor fields failed!\n"); goto error; } if(able_to_create_biopod_sensor_calibrator(&(s->calibrator))) { PDEBUG("Creation of biopod sensor calibrator failed!\n"); goto error; } if (able_to_set_default_vals_for_sensor(s)) { PDEBUG("Setting default values for sensors failed!\n"); goto error; } return 0; error: return 1; } static int able_to_create_phase_table_for(usb_biopod *dev) { if (!dev) return 1; dev->phase_table.num_rows = BIOPOD_PHASE_TABLE_NUM_ROWS; dev->phase_table.num_cols = BIOPOD_PHASE_TABLE_NUM_COLS; dev->phase_table.data = (matrix_data_t **)phase_table_data_3500; return 0; } static int able_to_create_gain_table_for(usb_biopod *dev) { if (!dev) return 1; dev->gain_table.num_rows = BIOPOD_GAIN_TABLE_NUM_ROWS; dev->gain_table.num_cols = BIOPOD_GAIN_TABLE_NUM_COLS; dev->gain_table.data = (matrix_data_t **)gain_table_data_3500; return 0; } static int able_to_create_four_to_eight_for(usb_biopod *dev) { if(!dev) { PDEBUG("Passing in null pointer to a function is BAD!!!%s:%d\n",__FILE__,__LINE__); return 1; } dev->four_to_eight = four_to_eight; return 0; } /* for now just set as four_to_eight */ static int able_to_create_pixel_table_for(usb_biopod *dev) { if (!dev || !(dev->four_to_eight)) { PDEBUG("Passing in null pointer to a function is BAD!!!%s:%d\n",__FILE__,__LINE__); return 1; } dev->pixel_table = dev->four_to_eight; return 0; } /* Create a biopod, no doubt */ static int able_to_create(struct usb_biopod *dev) { if (!dev) { /* this code should never happen because if dev is NULL, then it should have failed out alot earlier */ PDEBUG("Passing in null pointer to a function is BAD!!!%s:%d\n",__FILE__,__LINE__); return 1; } if (able_to_create_biopod_sensor_for(dev)) { PDEBUG("Creation of biopod sensor failed!\n"); goto error; } if (able_to_create_biopod_frame_for(dev)) { PDEBUG("Creation of biopod frame failed!\n"); goto error; } if (able_to_create_phase_table_for(dev)) { PDEBUG("Creation of biopod phase table failed!\n"); goto error; } if (able_to_create_four_to_eight_for(dev)) { PDEBUG("Creation of four to eight table failed!\n"); goto error; } if (able_to_create_pixel_table_for(dev)) { PDEBUG("Creation of pixel table failed!\n"); goto error; } if (able_to_create_gain_table_for(dev)) { PDEBUG("Creation of gain table failed!\n"); goto error; } return 0; error: return 1; } /* Free all member held memory - decrement ref count */ static void biopod_delete(struct kref *kref) { struct usb_biopod *dev; if(kref) { dev = to_biopod_dev(kref); /* Lock the datastructure while I free the memory so that * noone else can access the values after the memory has * been removed */ if(dev) { while(able_to_lock(dev)) ; /* wait */ usb_put_dev(dev->udev); free_all_biopod_member_data_in(dev); unlock(dev); PDEBUG("trying to free dev ...\n"); kfree (dev); PDEBUG("Success!\n"); } } } static int biopod_open(struct inode *inode, struct file *file) { struct usb_biopod *dev = NULL; struct usb_interface *interface; int subminor; int retval = 0, i; unsigned char buffer[BIOPOD_NUM_SENSOR_REGS * 2]; subminor = iminor(inode); interface = usb_find_interface(&biopod_driver, subminor); if (!interface) { err ("%s - error, can't find device for minor %d", __FUNCTION__, subminor); retval = -ENODEV; goto exit; } dev = usb_get_intfdata(interface); if (!dev) { retval = -ENODEV; goto exit; } if (able_to_lock(dev)) { return -ERESTARTSYS; } /* Is this first open? If so, create device */ if (already_exist(dev->pixel_table)) { for(i = 0; i < 8; i++) dev->frame.image.subarrays[i] = 0; PDEBUG("Device does not exist, creating device!\n"); if(able_to_create(dev)) { PDEBUG("Device was not able to be created!\n"); free_all_biopod_member_data_in(dev); retval = -ENOMEM; unlock(dev); goto exit; } /** * Ok, memory is correctly allocated. * Lets send the default values to the device so that * the device will work correctly at first read of a * fingerprint without a hitch. Even if you know the * correct input bytes to scan for an image, without * the default values, the device doesn't seem to * respond correctly * I could be wrong, it is not documented, and this * is from my experiments with the device. */ { send_register_and_value_to_device(0x80,1); } for(i = 0; i < BIOPOD_NUM_SENSOR_REGS * 2; i++) { sprintf(&buffer[i],"%c%c",0x80 + i, dev->sensor.regs.defaults[i]); } retval = usb_bulk_msg(dev->udev, usb_sndbulkpipe(dev->udev,dev->bulk_out.endpointAddr), buffer,BIOPOD_NUM_SENSOR_REGS * 2,&i,HZ); } /* increment our usage count for the device */ kref_get(&dev->kref); /* save our object in the file's private structure */ file->private_data = dev; unlock(dev); exit: return retval; } static int biopod_release(struct inode *inode, struct file *file) { struct usb_biopod *dev; dev = (struct usb_biopod *)file->private_data; if (dev == NULL) return -ENODEV; /* decrement the count on our device */ kref_put(&dev->kref, biopod_delete); return 0; } static int biopod_register_parser(biopod_sensor_registers *registers, unsigned char *buffer, size_t length) { int x = 0; int which = 0; int index = 128; unsigned char *buf = buffer; for(x = 0; x < length; x++,buf++) { if(which == 0 && *buf >= 0x80 && *buf <= (0x80 + registers->num)){ which = 1; index = (*buf & 0x7F); } else if(which == 1 && *buf < 0x80 && index < 128) { registers->runtime[index] = *buf; } else return -x; if(x >= 120) break; } return x; } static int biopod_image_parser(biopod_image *image, unsigned char *buffer, size_t length) { uint8_t row = 0, col = 0, pixel = 0; uint8_t startrow = 0, stoprow = 0; uint8_t header = 0, byte = 0; uint8_t subarray = 0; int x,y; unsigned char *data = image->data; unsigned char *buf = buffer; if(!data || !buffer) { PDEBUG("Data or buffer is NULL! data = %p, buffer = %p\n",data,buf); return -1; } PDEBUG("max bytes = %d\n",image->num_cols * image->num_rows); for(x = 0; x < length; x++,buf++) { if(header == 0 && *buf >=(unsigned char)0xE0 && *buf <= (unsigned char)0xE7) { subarray = (uint8_t)buffer[x] - 0xE0; if(image->subarrays[subarray] > 0){ continue; } image->subarrays[subarray]++; startrow = image->num_cols - (subarray * 16); stoprow = startrow - 15; row = startrow; pixel = image->num_cols; header = 1; } else if (header == 1) { data[row-- * col + pixel] = image->dev->four_to_eight[*buf & 0x0F]; data[row-- * col + pixel] = image->dev->four_to_eight[(*buf >> 4) & 0x0F]; if(row < stoprow || row > startrow) { if(pixel != 0) pixel--; row = startrow; } byte++; if(byte == image->num_cols * image->num_rows) { image->subarrays[subarray]++; if(subarray == 7) break; header = 0; byte = 0; } } } for(y = 0; y < 8; y++) { if(image->subarrays[y] == 0) { PDEBUG("Subarray %d is missing!\n",y); return -x; } } return x; } static int biopod_parser(struct usb_biopod *dev, unsigned char *buffer, size_t length) { int x; int retval; for(x = 0; x < length; x++) { if(buffer[x] >= (unsigned char)0xE0 && buffer[x] <= (unsigned char)0xE7) { if(down_interruptible(&dev->frame.image.image_mutex)) { PDEBUG("Unable to get image lock, try again!\n"); return 1; } retval = biopod_image_parser(&dev->frame.image,&buffer[x],length-x); up(&dev->frame.image.image_mutex); if(retval > 0) { PDEBUG("retval = %d, moving iter from %d to %d\n",retval,x,x+retval); x += retval; } else { return 1; } /*} else if(buffer[x] >= 0x80 && buffer[x] < (0x80 + dev->sensor.regs.num)) { if(down_interruptible(&dev->sensor.sensor_mutex)) { PDEBUG("Unable to get sensor lock!\n"); return 1; } retval = biopod_register_parser(&dev->sensor.regs,&buffer[x],length-x); up(&dev->sensor.sensor_mutex); if(retval > 0) { x+=retval; break; } else { return 1; } } else if (buffer[x] == 0xDE) {*/ /* Histogram code has not yet been implemented, since * we don't know what they are for, but the structure * has been added to the code so that if we ever figure * out what they are for, cleanup and shutdown won't * have to be dealt with or debugged */ } } return 0; } static int read_from_device(struct usb_biopod *dev, size_t count,unsigned char *temp) { size_t len = 0; size_t tmp_len = 0; if(!temp || !dev) return -1; while(len < count) { usb_bulk_msg(dev->udev, usb_rcvbulkpipe(dev->udev, dev->bulk_in.endpointAddr), dev->bulk_in.buffer, min(dev->bulk_in.size, count - len), &tmp_len, HZ * 5); len += min(dev->bulk_in.size, min(count - len,tmp_len)); memcpy(&temp[len],dev->bulk_in.buffer,tmp_len); if(tmp_len != 64) break; } return len; } static ssize_t biopod_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos) { struct usb_biopod *dev; int retval = 0; int len = 0; unsigned char *temp; int loopcounter = 0; temp = kmalloc(sizeof(*temp) * count, GFP_KERNEL); if(!temp) { return -ENOMEM; } PDEBUG("%s - buffer size is %d\n",current->comm,count); dev = (struct usb_biopod *)file->private_data; /* do a blocking bulk read to get data from the device */ loop: if((len = read_from_device(dev,count,temp)) < 0){ if(loopcounter < 1000) { loopcounter++; goto loop; } } /*PDEBUG("\"%s\" - reading: \n%s\n", current->comm,buffer);*/ if(biopod_parser(dev,temp,len)) { loopcounter++; if(loopcounter < 1000) { PDEBUG("Going to loop, parser failed!\n"); goto loop; } /* If we are not able to parse an image out of 10 tries successfully * then something is not coming through right. If a register or a * histogram has been requested, then it shouldn't fail, unless the * parser for histogram and register fail, which are less complicated * since most of the data can fit into 1 or 2 usb packets. An image * however, fits in (width * height / 64) packets, which for the 3400/3500 * is 128 packets +/- 1 packet. */ PDEBUG("After 1000 tries, a valid image has not been found.\n"); } /* if the read was successful, copy the data to userspace */ if (!retval || retval == -110) { if (copy_to_user(buffer, temp, len)) { kfree(temp); retval = -EFAULT; } else { retval = len; } } kfree(temp); return retval; } int biopod_ioctl(struct inode *inodep, struct file *filp, unsigned int code, unsigned long arg) { int err = 0; int retval = 0; int *val; struct usb_biopod *dev = (struct usb_biopod *)filp->private_data; /* * extract the type and number of bitfields and don't decode * wrong cmds: return ENOTTY (inappropriate ioctl) before access_ok() */ if(_IOC_TYPE(code) != BIOPOD_IOC_MAGIC) return -ENOTTY; if(_IOC_NR(code) > BIOPOD_IOC_MAXNR) return -ENOTTY; /* * the direction is a bitmask, and VERIFY_WRITE catches RW * transfers. `Type` is user oriented, while access_ok * is kernel-oriented, so the concept of "read" and * "write" is reversed */ if(_IOC_DIR(code) & _IOC_READ) err = !access_ok(VERIFY_WRITE, (void __user *)arg, _IOC_SIZE(code)); else if(_IOC_DIR(code) & _IOC_WRITE) err = !access_ok(VERIFY_READ, (void __user *)arg, _IOC_SIZE(code)); if(err) return -EFAULT; switch(code) { case BIOPOD_IOCRESET: send_register_and_value_to_device(0x80,0) break; case BIOPOD_IOCSTART: send_register_and_value_to_device(0x81,1) break; case BIOPOD_IOCSTOP: send_register_and_value_to_device(0x81,0) break; case BIOPOD_IOCDEFAULT: { int i; unsigned char buffer[BIOPOD_NUM_SENSOR_REGS * 2]; for(i = 0; i < BIOPOD_NUM_SENSOR_REGS * 2; i++) { sprintf(&buffer[i],"%c%c",0x80 + i, dev->sensor.regs.defaults[i]); } retval = usb_bulk_msg(dev->udev, usb_sndbulkpipe(dev->udev,dev->bulk_out.endpointAddr), buffer,BIOPOD_NUM_SENSOR_REGS * 2,&i,HZ); } break; case BIOPOD_IOCSINGLE: val = (void *)arg; if(*val > 1 && *val < 0) return -EINVAL; send_register_and_value_to_device(0x81,(*val << 2)); break; case BIOPOD_IOCASYNC: val = (void *)arg; if(*val > 1 && *val < 0) return -EINVAL; send_register_and_value_to_device(0x80,(*val << 2)); break; case BIOPOD_IOCHIST: val = (void *)arg; if(*val > 1 && *val < 0) return -EINVAL; send_register_and_value_to_device(0x98,(*val << 1)) case BIOPOD_IOCGAREG: val = (void *)arg; if(*val < 0x80 || *val > (0x80 + dev->sensor.regs.num)) return -EINVAL; else retval = dev->sensor.regs.runtime[*val - 0x80]; break; case BIOPOD_IOCQIMAGE: { struct biopod_image *tmpimage = (void *)arg; if(!tmpimage) return -EINVAL; tmpimage->num_cols = dev->frame.image.num_cols; tmpimage->num_rows = dev->frame.image.num_rows; tmpimage->id = dev->frame.image.id; } break; case BIOPOD_IOCQREGS: { struct biopod_sensor_registers *tmpreg = (void *)arg; if(!arg) return -EINVAL; tmpreg->num = dev->sensor.regs.num; } break; case BIOPOD_IOCGREGS: { struct biopod_sensor_registers *tmpreg = (void *)arg; int i = 0; int len = 0; uint8_t loopcount = 0; unsigned char *buffer; int count = BIOPOD_NUM_SENSOR_REGS * 2; if(!tmpreg) return -EINVAL; if(!tmpreg->runtime) return -EINVAL; buffer = kmalloc(count,GFP_KERNEL); if(!buffer) return -ENOMEM; memset(buffer,0,sizeof(*buffer)); regloop: send_register_and_value_to_device(0x80,2); if((len = read_from_device(dev,count,buffer)) < 0) { if(loopcount < 5) { loopcount++; goto regloop; } } if(biopod_register_parser(tmpreg,buffer,len)) { if(loopcount < 5) { loopcount++; PDEBUG("BIOPOD_IOCGREGS image retrieval failed, looping!\n"); goto regloop; } else retval = -EINVAL; } else { /* Try to copy the new register values into the global structure * so that they can be used without having to request them */ if(retval == 0 && !down_interruptible(&dev->sensor.sensor_mutex)) { for(i = 0; i < dev->sensor.regs.num; i++) { dev->sensor.regs.runtime[i] = tmpreg->runtime[i]; } up(&dev->sensor.sensor_mutex); } retval = 0; } } break; case BIOPOD_IOCGIMAGE: { int count = dev->frame.image.num_cols * dev->frame.image.num_rows + 8; struct biopod_image *tmpimage = (void *)arg; uint16_t loopcount = 0; int len = 0; unsigned char *buffer; if(!tmpimage) return -EINVAL; if(!tmpimage->data) return -EINVAL; buffer = kmalloc(count,GFP_KERNEL); if(!buffer) return -ENOMEM; memset(buffer,0,sizeof(*buffer)); tmpimage->dev = dev; iocloop: if((len = read_from_device(dev,count,buffer)) < 0) { if(loopcount < 1000) { loopcount++; goto iocloop; } } if(biopod_image_parser(tmpimage,buffer,len)) { if(loopcount < 1000) { loopcount++; PDEBUG("BIOPOD_IOCGIMAGE image retrieval failed, looping!\n"); goto iocloop; } else { retval = -EINVAL; } } kfree(buffer); } if(retval > 0) retval = 0; break; case BIOPOD_IOCGHIST: return -ENOTTY; break; case BIOPOD_IOCQHIST: return -ENOTTY; break; case BIOPOD_IOCDEVRESET: val = (void *)arg; if(*val > 1 && *val < 0) return -EINVAL; send_register_and_value_to_device(0x99,(*val << 2)); break; default: return -ENOTTY; } return retval; } static void biopod_write_bulk_callback(struct urb *urb, struct pt_regs *regs) { /* sync/async unlink faults aren't errors */ if (urb->status && !(urb->status == -ENOENT || urb->status == -ECONNRESET || urb->status == -ESHUTDOWN)) { dbg("%s - nonzero write bulk status received: %d", __FUNCTION__, urb->status); } /* free up our allocated buffer */ usb_buffer_free(urb->dev, urb->transfer_buffer_length, urb->transfer_buffer, urb->transfer_dma); } static ssize_t biopod_write(struct file *file, const char __user *user_buffer, size_t count, loff_t *ppos) { struct usb_biopod *dev; int retval = 0; struct urb *urb = NULL; char *buf = NULL; dev = (struct usb_biopod *)file->private_data; /* verify that we actually have some data to write */ if (count == 0) goto exit; /* create a urb, and a buffer for it, and copy the data to the urb */ urb = usb_alloc_urb(0, GFP_KERNEL); if (!urb) { retval = -ENOMEM; goto error; } buf = usb_buffer_alloc(dev->udev, count, GFP_KERNEL, &urb->transfer_dma); if (!buf) { retval = -ENOMEM; goto error; } if (copy_from_user(buf, user_buffer, count)) { retval = -EFAULT; goto error; } /* initialize the urb properly */ usb_fill_bulk_urb(urb, dev->udev, usb_sndbulkpipe(dev->udev, dev->bulk_out.endpointAddr), buf, count, biopod_write_bulk_callback, dev); urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; /* send the data out the bulk port */ retval = usb_submit_urb(urb, GFP_KERNEL); if (retval) { err("%s - failed submitting write urb, error %d", __FUNCTION__, retval); goto error; } /* release our reference to this urb, the USB core will eventually free it entirely */ usb_free_urb(urb); exit: return count; error: usb_buffer_free(dev->udev, count, buf, urb->transfer_dma); usb_free_urb(urb); kfree(buf); return retval; } static struct file_operations biopod_fops = { .owner = THIS_MODULE, .read = biopod_read, .write = biopod_write, .open = biopod_open, .ioctl = biopod_ioctl, .release = biopod_release, }; /* * usb class driver info in order to get a minor number from the usb core, * and to have the device registered with devfs and the driver core */ static struct usb_class_driver biopod_class = { .name = "usb/biopod%d", .fops = &biopod_fops, .mode = S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH, .minor_base = USB_BIOPOD_MINOR_BASE, }; static int biopod_probe(struct usb_interface *interface, const struct usb_device_id *id) { struct usb_biopod *dev = NULL; struct usb_host_interface *iface_desc; struct usb_endpoint_descriptor *endpoint; size_t buffer_size; int i; int retval = -ENOMEM; PDEBUG("\"%s\" - entering 'probe'\n", current->comm); /* allocate memory for our device state and initialize it */ dev = kmalloc(sizeof(struct usb_biopod), GFP_KERNEL); if (dev == NULL) { err("Out of memory"); goto error; } memset(dev, 0x00, sizeof (*dev)); kref_init(&dev->kref); dev->udev = usb_get_dev(interface_to_usbdev(interface)); dev->interface = interface; /* set up the endpoint information */ /* use only the first bulk-in and bulk-out endpoints */ iface_desc = interface->cur_altsetting; for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { endpoint = &iface_desc->endpoint[i].desc; if (!dev->bulk_in.endpointAddr && (endpoint->bEndpointAddress & USB_DIR_IN) && ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_BULK)) { /* we found a bulk in endpoint */ buffer_size = endpoint->wMaxPacketSize; dev->bulk_in.size = buffer_size; dev->bulk_in.endpointAddr = endpoint->bEndpointAddress; dev->bulk_in.buffer = kmalloc(buffer_size, GFP_KERNEL); if (!dev->bulk_in.buffer) { err("Could not allocate bulk_in.buffer"); goto error; } PDEBUG("\"%s\" - 'probe': after init of in endpoint %d:\n", current->comm, i); PDEBUG("\"%s\" - bulk_in.size: %d\n", current->comm, dev->bulk_in.size); PDEBUG("\"%s\" - bulk_in.endpointAddr %X\n", current->comm, dev->bulk_in.endpointAddr); } if (!dev->bulk_out.endpointAddr && !(endpoint->bEndpointAddress & USB_DIR_IN) && ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_BULK)) { /* we found a bulk out endpoint */ dev->bulk_out.endpointAddr = endpoint->bEndpointAddress; } PDEBUG("\"%s\" - 'probe': after init of out endpoint %d:\n", current->comm, i); PDEBUG("\"%s\" - bulk_out_endpointAddr %X\n", current->comm, dev->bulk_out.endpointAddr); } if (!(dev->bulk_in.endpointAddr && dev->bulk_out.endpointAddr)) { err("Could not find both bulk-in and bulk-out endpoints"); goto error; } /* save our data pointer in this interface device */ usb_set_intfdata(interface, dev); /* initialize the mutex (sema4) because we will need it for opens */ init_MUTEX(&(dev->biopod_mutex)); init_MUTEX(&(dev->sensor.sensor_mutex)); init_MUTEX(&(dev->frame.frame_mutex)); init_MUTEX(&(dev->bulk_in.bulk_in_mutex)); init_MUTEX(&(dev->frame.histos.histogram_mutex)); init_MUTEX(&(dev->frame.image.image_mutex)); init_MUTEX(&(dev->frame.regs.register_intf_mutex)); /* !!! Do we need to do this (because we memsetted 0 above)? */ dev->four_to_eight = NULL; dev->pixel_table = NULL; /* we can register the device now, as it is ready */ retval = usb_register_dev(interface, &biopod_class); if (retval) { /* something prevented us from registering this driver */ err("Not able to get a minor for this device."); usb_set_intfdata(interface, NULL); goto error; } /* let the user know what node this device is now attached to */ info("USB Biopod device now attached to USBBiopod-%d", interface->minor); return 0; error: if (dev) { kref_put(&dev->kref, biopod_delete); } PDEBUG("\"%s\" - end 'probe'\n", current->comm); return retval; } static void biopod_disconnect(struct usb_interface *interface) { struct usb_biopod *dev; int minor = interface->minor; /* prevent biopod_open() from racing biopod_disconnect() */ lock_kernel(); dev = usb_get_intfdata(interface); usb_set_intfdata(interface, NULL); /* give back our minor */ usb_deregister_dev(interface, &biopod_class); unlock_kernel(); /* decrement our usage count */ kref_put(&dev->kref, biopod_delete); info("USB Biopod #%d now disconnected", minor); } static struct usb_driver biopod_driver = { .owner = THIS_MODULE, .name = "biopod", .id_table = biopod_table, .probe = biopod_probe, .disconnect = biopod_disconnect, }; static int __init usb_biopod_init(void) { int result; /* register this driver with the USB subsystem */ result = usb_register(&biopod_driver); if (result) err("usb_register failed. Error number %d", result); return result; } static void __exit usb_biopod_exit(void) { /* deregister this driver with the USB subsystem */ usb_deregister(&biopod_driver); } module_init (usb_biopod_init); module_exit (usb_biopod_exit); MODULE_LICENSE("GPL");