SHARE
TWEET

FM transmitter frequency set tool for Raspberry Pi

a guest Dec 21st, 2012 427 Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. // Raspberry Pi frequency set tool for the B-Link / Keene FM transmitter
  2. // Original source from: https://github.com/kenchy/keene-usb-audio
  3. // To compile: gcc -o fmtx fmtx.c -lusb
  4. // If you're missing usb.h make sure libusb-dev is installed
  5. // Due to low level USB access it must run from as root or via sudo
  6. //
  7. // source code modifications by redhawk 20/12/2012:
  8. // fixed - "Segmentation fault" due to out of range frequency (usb handle closed wasn't actually opened)
  9. // added - set frequency with decimal value
  10. // added - option to power off transmitter
  11. // added - stereo / mono selection
  12. // added - 50us / 75us pre-emphasis selection
  13. // added - transmission power adjustment
  14. // added - TX volume control
  15. // added - DSP volume control (via aplay and amixer)
  16. // changed - error and help messages
  17. // changed - lowered TX volume this helped to reduce background noise from PI USB power supply
  18.  
  19. #include <stdio.h>
  20. #include <string.h>
  21. #include <usb.h>
  22. #include <errno.h>
  23.  
  24. extern int errnol;
  25. usb_dev_handle  *keenehandle;
  26.  
  27. #define keeneVID  0x046d
  28. #define keenePID  0x0a0e
  29. #define tx_min_freq 8750  // Minimum frequency: Japan = 7600, USA = 8810,  UK/EU = 8750
  30. #define tx_max_freq 10800 // Maximum frequency: Japan = 9000, USA = 10790, UK/EU = 10800
  31. #define tx_off_freq 11400 // Out of range frequency will actual turn off the Transmitter
  32.  
  33. // ** cleanup() *****************************************************
  34. void cleanup() {
  35.     usb_set_debug(0);
  36.     // if we have the USB device, hand it back
  37.     if (keenehandle) {
  38.         usb_release_interface(keenehandle,2);
  39.         usb_close(keenehandle);
  40.     }
  41.     exit(errno);
  42. }
  43.  
  44. void set_dsp(int volume_level) {
  45.   FILE *fp;
  46.   char output[2];
  47.   char *cmd;
  48.   int card_no = -1;
  49.   int vol = volume_level;
  50.   fp = popen("aplay -l | fgrep -i B-Link | awk '{ print substr($2,1,1) }'","r");
  51.   fgets(output, sizeof output, fp);
  52.   if (strcmp(output, "0") == 0) { card_no = 0; }
  53.   if (atoi(output) > 0) { card_no = atoi(output); }
  54.   pclose(fp);
  55.   if (card_no == -1) {
  56.     printf("Error: B-Link audio DSP not found\n");
  57.     exit(1);
  58.   }
  59.   if (vol > 56) { vol = 56; }
  60.   if (vol < 0) { vol = 0; }
  61.   char str_vol[2];
  62.   sprintf(str_vol, "%d", vol);
  63.   strcpy(cmd,"amixer -c");
  64.   strcat(cmd, output);
  65.   strcat(cmd, " set PCM ");
  66.   strcat(cmd, str_vol);
  67.   strcat(cmd, " >/dev/null");
  68.   system(cmd);
  69.   exit(0);
  70. }
  71.  
  72. usb_dev_handle * GetFirstDevice(int vendorid, int productid) {
  73.   struct usb_bus    *bus;
  74.   struct usb_device *dev;
  75.  
  76.   usb_dev_handle    *device_handle = NULL; // it's a null
  77.  
  78.   usb_init();
  79.   usb_set_debug(0);     /* 3 is the max setting */
  80.  
  81.   usb_find_busses();
  82.   usb_find_devices();
  83.  
  84.   // Loop through each bus and device until you find the first match
  85.   // open it and return it's usb device handle
  86.  
  87.   for (bus = usb_busses; bus; bus = bus->next) {
  88.     for (dev = bus->devices; dev; dev = dev->next) {
  89.       //if (dev->descriptor.idVendor == vendorid && dev->descriptor.idProduct == productid) {
  90.       if (dev->descriptor.idVendor == vendorid) {
  91.     device_handle = usb_open(dev);
  92.     //
  93.     // No device found
  94.     if(!device_handle) {
  95.       return NULL;
  96.     }
  97. //    fprintf(stderr," + Found audio transmiter on bus %s dev %s\n", dev->bus->dirname, dev->filename);
  98.     return (device_handle);
  99.       }
  100.     }
  101.   }
  102.   errno = ENOENT;       // No such device
  103.   return (device_handle);
  104. }
  105.  
  106. int help(char *prog, int more_options) {
  107.  int freq_imin = tx_min_freq;
  108.  int freq_imax = tx_max_freq;
  109.  double freq_dmin = tx_min_freq / 100;
  110.  double freq_dmax = tx_max_freq / 100;
  111. if (more_options == 0) {
  112.    printf("Usage: %s freq (MHz) i.e. 88.1, 99, 106.95\n",prog);
  113.    printf("Or: %s --help (for more options)\n\n",prog);
  114.   return 1;
  115. } else
  116.  {
  117.    printf("Usage: %s [freq] [options]\n\n",prog);
  118.    printf("[freq] [%.2f - %.2f](MHz) 88.1, 99, 106.95\n",freq_dmin,freq_dmax);
  119.    printf("[freq] [%i - %i] as 8810, 9900, 10695\n",freq_imin, freq_imax);
  120.    printf("[freq] off (disable FM transmitter)\n\n");
  121.    printf("[Options] can omit [freq] except for DSP and TX power\n");
  122.    printf("(FM mode) s, stereo,  m,  mono (default = Stereo)\n");
  123.    printf("(Pre-emphasis) 50, 50us, 75, 75us (default = 50us)\n");
  124.    printf("(Audio volume) v0, v1, v2, v3, v4, v5 (default = v1)\n");
  125.    printf("(DSP volume) dsp [0 - 56] (default = dsp 52)\n");
  126.    printf("[freq] (TX power) l, low, h, high (default = high)\n\n");
  127.    return 0;
  128.  }
  129. }
  130. int keene_sendget(usb_dev_handle *handle, char *senddata ) {
  131.     unsigned char buf[64];
  132.     int rc;
  133.  
  134.     memcpy(buf, senddata, 0x0000008);
  135.     rc = usb_control_msg(handle, USB_TYPE_CLASS + USB_RECIP_INTERFACE, 0x0000009, 0x0000200, 0x0000002, buf, 0x0000008, 1000);
  136.     //printf("30 control msg returned %d", rc);
  137.     //printf("\n");
  138.  
  139.     if(rc < 0) {
  140.             perror(" ! Transfer error");
  141.         cleanup();
  142.     }
  143. }
  144.  
  145. // ** main() ********************************************************
  146. int main(int argc, char *argv[]) {
  147.     int rc;
  148.     int freq_h;
  149.     int freq_l;
  150.     int freq_in = 0;
  151.     int set_mode = 0;
  152.     int valid_arg = 0;
  153.     int tx_power = 0x78; // TX power (device power is either high or low it cannot adjust)
  154.     int tx_pmax = 0x78; // 120
  155.     int tx_pmin = 0x1e; // 30
  156.     int tx_mode = 0x20; // steps: 0x00 = 0.2MHz, 0x10 = 0.1MHz, 0x20 = 0.05MHz
  157.     int tx_volume = 2; // TX volume control (not DSP)
  158.     if ((argc < 2) || (argc > 6)) {
  159.       return help(argv[0],0);
  160.     }
  161.     freq_in = atoi(argv[1]);
  162.     set_mode = 0;
  163.     if ((argc > 2) && (strcmp(argv[1], "dsp") == 0)) { set_dsp(atoi(argv[2])); }
  164.     if ((argc > 1) && (strcmp(argv[1], "m") == 0)) { set_mode = 1; valid_arg = 1; } // check for mono
  165.     if ((argc > 2) && (strcmp(argv[2], "m") == 0)) { set_mode = 1; valid_arg = 1; }
  166.     if ((argc > 3) && (strcmp(argv[3], "m") == 0)) { set_mode = 1; valid_arg = 1; }
  167.     if ((argc > 4) && (strcmp(argv[4], "m") == 0)) { set_mode = 1; valid_arg = 1; }
  168.     if ((argc > 5) && (strcmp(argv[5], "m") == 0)) { set_mode = 1; valid_arg = 1; }
  169.     if ((argc > 1) && (strcmp(argv[1], "mono") == 0)) { set_mode = 1; valid_arg = 1; }
  170.     if ((argc > 2) && (strcmp(argv[2], "mono") == 0)) { set_mode = 1; valid_arg = 1; }
  171.     if ((argc > 3) && (strcmp(argv[3], "mono") == 0)) { set_mode = 1; valid_arg = 1; }
  172.     if ((argc > 4) && (strcmp(argv[4], "mono") == 0)) { set_mode = 1; valid_arg = 1; }
  173.     if ((argc > 5) && (strcmp(argv[5], "mono") == 0)) { set_mode = 1; valid_arg = 1; }
  174.  
  175.     if ((argc > 1) && (strcmp(argv[1], "s") == 0)) { set_mode = 0; valid_arg = 1; } // check for stereo
  176.     if ((argc > 2) && (strcmp(argv[2], "s") == 0)) { set_mode = 0; valid_arg = 1; }
  177.     if ((argc > 3) && (strcmp(argv[3], "s") == 0)) { set_mode = 0; valid_arg = 1; }
  178.     if ((argc > 4) && (strcmp(argv[4], "s") == 0)) { set_mode = 0; valid_arg = 1; }
  179.     if ((argc > 5) && (strcmp(argv[5], "s") == 0)) { set_mode = 0; valid_arg = 1; }
  180.     if ((argc > 1) && (strcmp(argv[1], "stereo") == 0)) { set_mode = 0; valid_arg = 1; }
  181.     if ((argc > 2) && (strcmp(argv[2], "stereo") == 0)) { set_mode = 0; valid_arg = 1; }
  182.     if ((argc > 3) && (strcmp(argv[3], "stereo") == 0)) { set_mode = 0; valid_arg = 1; }
  183.     if ((argc > 4) && (strcmp(argv[4], "stereo") == 0)) { set_mode = 0; valid_arg = 1; }
  184.     if ((argc > 5) && (strcmp(argv[5], "stereo") == 0)) { set_mode = 0; valid_arg = 1; }
  185.  
  186.     tx_mode = tx_mode + set_mode;
  187.     set_mode = 0;
  188.  
  189.     if ((argc > 1) && (strcmp(argv[1], "75us") == 0)) { set_mode = 4; valid_arg = 1; } // check for 75us pre-emphasis
  190.     if ((argc > 2) && (strcmp(argv[2], "75us") == 0)) { set_mode = 4; valid_arg = 1; }
  191.     if ((argc > 3) && (strcmp(argv[3], "75us") == 0)) { set_mode = 4; valid_arg = 1; }
  192.     if ((argc > 4) && (strcmp(argv[4], "75us") == 0)) { set_mode = 4; valid_arg = 1; }
  193.     if ((argc > 5) && (strcmp(argv[5], "75us") == 0)) { set_mode = 4; valid_arg = 1; }
  194.     if ((argc > 1) && (strcmp(argv[1], "75") == 0)) { set_mode = 4; valid_arg = 1; }
  195.     if ((argc > 2) && (strcmp(argv[2], "75") == 0)) { set_mode = 4; valid_arg = 1; }
  196.     if ((argc > 3) && (strcmp(argv[3], "75") == 0)) { set_mode = 4; valid_arg = 1; }
  197.     if ((argc > 4) && (strcmp(argv[4], "75") == 0)) { set_mode = 4; valid_arg = 1; }
  198.     if ((argc > 5) && (strcmp(argv[5], "75") == 0)) { set_mode = 4; valid_arg = 1; }
  199.  
  200.     if ((argc > 1) && (strcmp(argv[1], "50us") == 0)) { set_mode = 0; valid_arg = 1; } // check for 50us pre-emphasis
  201.     if ((argc > 2) && (strcmp(argv[2], "50us") == 0)) { set_mode = 0; valid_arg = 1; }
  202.     if ((argc > 3) && (strcmp(argv[3], "50us") == 0)) { set_mode = 0; valid_arg = 1; }
  203.     if ((argc > 4) && (strcmp(argv[4], "50us") == 0)) { set_mode = 0; valid_arg = 1; }
  204.     if ((argc > 5) && (strcmp(argv[5], "50us") == 0)) { set_mode = 0; valid_arg = 1; }
  205.     if ((argc > 1) && (strcmp(argv[1], "50") == 0)) { set_mode = 0; valid_arg = 1; }
  206.     if ((argc > 2) && (strcmp(argv[2], "50") == 0)) { set_mode = 0; valid_arg = 1; }
  207.     if ((argc > 3) && (strcmp(argv[3], "50") == 0)) { set_mode = 0; valid_arg = 1; }
  208.     if ((argc > 4) && (strcmp(argv[4], "50") == 0)) { set_mode = 0; valid_arg = 1; }
  209.     if ((argc > 5) && (strcmp(argv[5], "50") == 0)) { set_mode = 0; valid_arg = 1; }
  210.  
  211.     if ((argc > 1) && (strcmp(argv[1], "v0") == 0)) { tx_volume = 3; valid_arg = 1; } // volume preset 0
  212.     if ((argc > 2) && (strcmp(argv[2], "v0") == 0)) { tx_volume = 3; valid_arg = 1; }
  213.     if ((argc > 3) && (strcmp(argv[3], "v0") == 0)) { tx_volume = 3; valid_arg = 1; }
  214.     if ((argc > 4) && (strcmp(argv[4], "v0") == 0)) { tx_volume = 3; valid_arg = 1; }
  215.     if ((argc > 5) && (strcmp(argv[5], "v0") == 0)) { tx_volume = 3; valid_arg = 1; }
  216.  
  217.     if ((argc > 1) && (strcmp(argv[1], "v1") == 0)) { tx_volume = 2; valid_arg = 1; } // volume preset 1
  218.     if ((argc > 2) && (strcmp(argv[2], "v1") == 0)) { tx_volume = 2; valid_arg = 1; }
  219.     if ((argc > 3) && (strcmp(argv[3], "v1") == 0)) { tx_volume = 2; valid_arg = 1; }
  220.     if ((argc > 4) && (strcmp(argv[4], "v1") == 0)) { tx_volume = 2; valid_arg = 1; }
  221.     if ((argc > 5) && (strcmp(argv[5], "v1") == 0)) { tx_volume = 2; valid_arg = 1; }
  222.  
  223.     if ((argc > 1) && (strcmp(argv[1], "v2") == 0)) { tx_volume = 4; valid_arg = 1; } // volume preset 2
  224.     if ((argc > 2) && (strcmp(argv[2], "v2") == 0)) { tx_volume = 4; valid_arg = 1; }
  225.     if ((argc > 3) && (strcmp(argv[3], "v2") == 0)) { tx_volume = 4; valid_arg = 1; }
  226.     if ((argc > 4) && (strcmp(argv[4], "v2") == 0)) { tx_volume = 4; valid_arg = 1; }
  227.     if ((argc > 5) && (strcmp(argv[5], "v2") == 0)) { tx_volume = 4; valid_arg = 1; }
  228.  
  229.     if ((argc > 1) && (strcmp(argv[1], "v3") == 0)) { tx_volume = 16; valid_arg = 1; } // volume preset 3
  230.     if ((argc > 2) && (strcmp(argv[2], "v3") == 0)) { tx_volume = 16; valid_arg = 1; }
  231.     if ((argc > 3) && (strcmp(argv[3], "v3") == 0)) { tx_volume = 16; valid_arg = 1; }
  232.     if ((argc > 4) && (strcmp(argv[4], "v3") == 0)) { tx_volume = 16; valid_arg = 1; }
  233.     if ((argc > 5) && (strcmp(argv[5], "v3") == 0)) { tx_volume = 16; valid_arg = 1; }
  234.  
  235.     if ((argc > 1) && (strcmp(argv[1], "v4") == 0)) { tx_volume = 48; valid_arg = 1; } // volume preset 4
  236.     if ((argc > 2) && (strcmp(argv[2], "v4") == 0)) { tx_volume = 48; valid_arg = 1; }
  237.     if ((argc > 3) && (strcmp(argv[3], "v4") == 0)) { tx_volume = 48; valid_arg = 1; }
  238.     if ((argc > 4) && (strcmp(argv[4], "v4") == 0)) { tx_volume = 48; valid_arg = 1; }
  239.     if ((argc > 5) && (strcmp(argv[5], "v4") == 0)) { tx_volume = 48; valid_arg = 1; }
  240.  
  241.     if ((argc > 1) && (strcmp(argv[1], "v5") == 0)) { tx_volume = 80; valid_arg = 1; } // volume preset 5
  242.     if ((argc > 2) && (strcmp(argv[2], "v5") == 0)) { tx_volume = 80; valid_arg = 1; }
  243.     if ((argc > 3) && (strcmp(argv[3], "v5") == 0)) { tx_volume = 80; valid_arg = 1; }
  244.     if ((argc > 4) && (strcmp(argv[4], "v5") == 0)) { tx_volume = 80; valid_arg = 1; }
  245.     if ((argc > 5) && (strcmp(argv[5], "v5") == 0)) { tx_volume = 80; valid_arg = 1; }
  246.  
  247.     tx_mode = tx_mode + set_mode;
  248.  
  249.     if (strcmp(argv[1], "--help") == 0) {
  250.         return help(argv[0],1);
  251.     }
  252.     double freq_d = strtod(argv[1],NULL);
  253.     if (strcmp(argv[1], "off") == 0) { freq_in = tx_off_freq; tx_power = tx_pmin; valid_arg =2; }
  254.     if ( freq_in < 200 ) { freq_in =(int)(freq_d * 100); }  // if < 200 assume decimal value is used
  255.     if ((valid_arg == 0) && (freq_in != tx_off_freq)) {     // don't check range if special arguments are used
  256.     if ((freq_in < tx_min_freq)||(freq_in > tx_max_freq)) { // check for frequency in range
  257.           printf("Frequency out of range\n");
  258.           cleanup();
  259.         }
  260.       }
  261.     if ((argc > 1) && (strcmp(argv[1], "high") == 0)) { tx_power = tx_pmax; valid_arg = 3; } // check for high TX power
  262.     if ((argc > 2) && (strcmp(argv[2], "high") == 0)) { tx_power = tx_pmax; valid_arg = 3; }
  263.     if ((argc > 3) && (strcmp(argv[3], "high") == 0)) { tx_power = tx_pmax; valid_arg = 3; }
  264.     if ((argc > 4) && (strcmp(argv[4], "high") == 0)) { tx_power = tx_pmax; valid_arg = 3; }
  265.     if ((argc > 5) && (strcmp(argv[5], "high") == 0)) { tx_power = tx_pmax; valid_arg = 3; }
  266.     if ((argc > 6) && (strcmp(argv[6], "high") == 0)) { tx_power = tx_pmax; valid_arg = 3; }
  267.     if ((argc > 1) && (strcmp(argv[1], "h") == 0)) { tx_power = tx_pmax; valid_arg = 3; }
  268.     if ((argc > 2) && (strcmp(argv[2], "h") == 0)) { tx_power = tx_pmax; valid_arg = 3; }
  269.     if ((argc > 3) && (strcmp(argv[3], "h") == 0)) { tx_power = tx_pmax; valid_arg = 3; }
  270.     if ((argc > 4) && (strcmp(argv[4], "h") == 0)) { tx_power = tx_pmax; valid_arg = 3; }
  271.     if ((argc > 5) && (strcmp(argv[5], "h") == 0)) { tx_power = tx_pmax; valid_arg = 3; }
  272.     if ((argc > 6) && (strcmp(argv[6], "h") == 0)) { tx_power = tx_pmax; valid_arg = 3; }
  273.     if ((argc > 1) && (strcmp(argv[1], "low") == 0)) { tx_power = tx_pmin; valid_arg = 3; } // check for low TX power
  274.     if ((argc > 2) && (strcmp(argv[2], "low") == 0)) { tx_power = tx_pmin; valid_arg = 3; }
  275.     if ((argc > 3) && (strcmp(argv[3], "low") == 0)) { tx_power = tx_pmin; valid_arg = 3; }
  276.     if ((argc > 4) && (strcmp(argv[4], "low") == 0)) { tx_power = tx_pmin; valid_arg = 3; }
  277.     if ((argc > 5) && (strcmp(argv[5], "low") == 0)) { tx_power = tx_pmin; valid_arg = 3; }
  278.     if ((argc > 6) && (strcmp(argv[6], "low") == 0)) { tx_power = tx_pmin; valid_arg = 3; }
  279.     if ((argc > 1) && (strcmp(argv[1], "l") == 0)) { tx_power = tx_pmin; valid_arg = 3; }
  280.     if ((argc > 2) && (strcmp(argv[2], "l") == 0)) { tx_power = tx_pmin; valid_arg = 3; }
  281.     if ((argc > 3) && (strcmp(argv[3], "l") == 0)) { tx_power = tx_pmin; valid_arg = 3; }
  282.     if ((argc > 4) && (strcmp(argv[4], "l") == 0)) { tx_power = tx_pmin; valid_arg = 3; }
  283.     if ((argc > 5) && (strcmp(argv[5], "l") == 0)) { tx_power = tx_pmin; valid_arg = 3; }
  284.     if ((argc > 6) && (strcmp(argv[6], "l") == 0)) { tx_power = tx_pmin; valid_arg = 3; }
  285.  
  286.     if ((valid_arg == 3) && (argc == 2)) {
  287.       printf("Error: you must set the frequency before adjusting TX power\n");
  288.       return 1;
  289.     }
  290.  
  291.     unsigned char hexdata[64];
  292.     freq_in = ((freq_in - 7600)/5); // convert to counter ($0000 = 76MHz, $0001 = 76.05MHz etc)
  293.     freq_h = (freq_in / 256);
  294.     freq_l = (freq_in - (freq_h * 256));
  295.     hexdata[0]=0x00;
  296.     hexdata[1]=0x50;
  297.     hexdata[2]=freq_h;
  298.     hexdata[3]=freq_l;
  299.     hexdata[4]=tx_power;
  300.     hexdata[5]=0x19;
  301.     hexdata[6]=0x00;
  302.     hexdata[7]=0x44; // 0x44 tune up, 0x4D tune down
  303.     keenehandle = GetFirstDevice(keeneVID,keenePID);
  304.     //fprintf (stderr,"%d freq_3 %x freqhex %s hexdata \n",freq_3,freq_3,hexdata);
  305.  
  306.     keenehandle = GetFirstDevice(keeneVID,keenePID);
  307.  
  308.     // Find the USB connect
  309.     if (!keenehandle) {
  310.         perror(" ! Error opening device!");
  311.         exit(errno);
  312.     }
  313.  
  314.     // See if we can talk to the device.
  315.     rc = usb_claim_interface(keenehandle,2);
  316.     // If not, we might need to wrestle the keene off the HID driver
  317.     if (rc==-16) {
  318.             //need to grab the device the second bit of the HID device
  319.             rc = usb_detach_kernel_driver_np(keenehandle,2);
  320.             if(rc < 0) {
  321.                 perror(" ! usbhid wouldn't let go?");
  322.                 cleanup();
  323.             }
  324.         // try again
  325.         rc = usb_claim_interface(keenehandle,2);
  326.     }
  327.  
  328.     // Claim the interface
  329.     rc = usb_claim_interface(keenehandle,2);
  330.     if(rc < 0) {
  331.         perror(" ! Error claiming the interface(2) - you must run with root privileges");
  332.         cleanup();
  333.     }
  334.  
  335.     if (freq_in > 0) {
  336.       if (valid_arg > 1) { // change tx power by tuning frequency out and in
  337.         int temp_h,temp_l; // it cannot adjust power if frequency is unchanged
  338.         temp_h = hexdata[2];
  339.         temp_l = hexdata[3];
  340.         freq_in = ((tx_off_freq - 7600)/5);
  341.         freq_h = (freq_in / 256);
  342.         freq_l = (freq_in - (freq_h * 256));
  343.         hexdata[2]=freq_h;
  344.         hexdata[3]=freq_l;
  345.         keene_sendget(keenehandle,hexdata); // set TX to tx_freq_off
  346.         hexdata[2] = temp_h;
  347.         hexdata[3] = temp_l;
  348.       }
  349.       keene_sendget(keenehandle,hexdata); // set TX frequency
  350.     }
  351.     hexdata[0]=0x00;
  352.     hexdata[1]=0x51;
  353.     hexdata[2]=tx_volume;
  354.     hexdata[3]=tx_mode;
  355.     hexdata[4]=0x00;
  356.     hexdata[5]=0x00;
  357.     hexdata[6]=0x00;
  358.     hexdata[7]=0x44;
  359.     keene_sendget(keenehandle,hexdata); // set TX options
  360.  
  361.     cleanup();
  362. }
RAW Paste Data
Top